From db455780aa1a75186b2d72e8cfddf5fd09fcbb1a Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Thu, 21 Aug 2025 10:33:43 -0700 Subject: [PATCH 01/15] auto commit --- .../PlaintextToAWSDBE/MigrationUtils.cs | 83 +++++++++++++ .../awsdbe/MigrationStep1.cs | 40 +++++++ .../awsdbe/MigrationStep2.cs | 40 +++++++ .../awsdbe/MigrationStep3.cs | 40 +++++++ .../plaintext/MigrationStep0.cs | 109 ++++++++++++++++++ .../plaintext/MigrationStep0Test.cs | 76 ++++++++++++ 6 files changed, 388 insertions(+) create mode 100644 Examples/runtimes/net/src/migration/PlaintextToAWSDBE/MigrationUtils.cs create mode 100644 Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs create mode 100644 Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2.cs create mode 100644 Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3.cs create mode 100644 Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0.cs create mode 100644 Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0Test.cs diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/MigrationUtils.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/MigrationUtils.cs new file mode 100644 index 000000000..4fb02afc7 --- /dev/null +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/MigrationUtils.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using Amazon.DynamoDBv2.Model; + +namespace Examples.migration.PlaintextToAWSDBE +{ + /* + Utility class for the PlaintextToAWSDBE migration examples. + This class contains shared functionality used by all migration steps. + */ + public class MigrationUtils + { + // Verify that a returned item matches the expected values + public static bool VerifyReturnedItem(GetItemResponse response, string partitionKeyValue, string sortKeyValue, + string encryptedAndSignedValue, string signOnlyValue, string doNothingValue) + { + var item = response.Item; + + // Verify partition key + if (!item.ContainsKey("partition_key") || item["partition_key"].S != partitionKeyValue) + { + throw new Exception($"partition_key mismatch: expected {partitionKeyValue}, got {(item.ContainsKey("partition_key") ? item["partition_key"].S : "null")}"); + } + + // Verify sort key + if (!item.ContainsKey("sort_key") || item["sort_key"].N != sortKeyValue) + { + throw new Exception($"sort_key mismatch: expected {sortKeyValue}, got {(item.ContainsKey("sort_key") ? item["sort_key"].N : "null")}"); + } + + // Verify attribute1 (will be encrypted and signed in future steps) + if (!item.ContainsKey("attribute1") || item["attribute1"].S != encryptedAndSignedValue) + { + throw new Exception($"attribute1 mismatch: expected {encryptedAndSignedValue}, got {(item.ContainsKey("attribute1") ? item["attribute1"].S : "null")}"); + } + + // Verify attribute2 (will be signed but not encrypted in future steps) + if (!item.ContainsKey("attribute2") || item["attribute2"].S != signOnlyValue) + { + throw new Exception($"attribute2 mismatch: expected {signOnlyValue}, got {(item.ContainsKey("attribute2") ? item["attribute2"].S : "null")}"); + } + + // Verify attribute3 (will neither be encrypted nor signed in future steps) + if (!item.ContainsKey("attribute3") || item["attribute3"].S != doNothingValue) + { + throw new Exception($"attribute3 mismatch: expected {doNothingValue}, got {(item.ContainsKey("attribute3") ? item["attribute3"].S : "null")}"); + } + + return true; + } + + // Helper method to clean up test items + public static async System.Threading.Tasks.Task CleanupItems(string tableName, string partitionKey, string[] sortKeys) + { + var ddb = new Amazon.DynamoDBv2.AmazonDynamoDBClient(); + + foreach (var sortKey in sortKeys) + { + try + { + var key = new Dictionary + { + ["partition_key"] = new AttributeValue { S = partitionKey }, + ["sort_key"] = new AttributeValue { N = sortKey } + }; + + var deleteRequest = new DeleteItemRequest + { + TableName = tableName, + Key = key + }; + + await ddb.DeleteItemAsync(deleteRequest); + } + catch (Exception e) + { + // Log but don't fail if cleanup fails + Console.WriteLine($"Warning: Failed to clean up test item with sort key {sortKey}: {e.Message}"); + } + } + } + } +} diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs new file mode 100644 index 000000000..84e7789b0 --- /dev/null +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Amazon.DynamoDBv2; +using Amazon.DynamoDBv2.Model; +using System.Diagnostics; +using System.Net; +using AWS.Cryptography.DbEncryptionSDK.DynamoDb; +using AWS.Cryptography.DbEncryptionSDK.StructuredEncryption; +using AWS.Cryptography.MaterialProviders; +using Examples.migration.PlaintextToAWSDBE; + +namespace Examples.migration.PlaintextToAWSDBE.awsdbe +{ + /* + Migration Step 1: This is the first step in the migration process from + plaintext to encrypted DynamoDB using the AWS Database Encryption SDK. + + This step will be implemented in the future. It will demonstrate how to: + 1. Configure a DynamoDB client with encryption capabilities + 2. Write items that can still be read by plaintext clients (Step 0) + 3. Read both plaintext items and items written by this step + + Running this example requires access to the DDB Table whose name + is provided in the function parameter. + This table must be configured with the following + primary key configuration: + - Partition key is named "partition_key" with type (S) + - Sort key is named "sort_key" with type (S) + */ + public class MigrationStep1 + { + // This method will be implemented in the future + public static async Task MigrationStep1Example(string kmsKeyId, string ddbTableName, string partitionKeyValue, string sortKeyWriteValue, string sortKeyReadValue) + { + // TODO: Implement MigrationStep1 + throw new NotImplementedException("MigrationStep1 is not yet implemented"); + } + } +} diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2.cs new file mode 100644 index 000000000..090c43404 --- /dev/null +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Amazon.DynamoDBv2; +using Amazon.DynamoDBv2.Model; +using System.Diagnostics; +using System.Net; +using AWS.Cryptography.DbEncryptionSDK.DynamoDb; +using AWS.Cryptography.DbEncryptionSDK.StructuredEncryption; +using AWS.Cryptography.MaterialProviders; +using Examples.migration.PlaintextToAWSDBE; + +namespace Examples.migration.PlaintextToAWSDBE.awsdbe +{ + /* + Migration Step 2: This is the second step in the migration process from + plaintext to encrypted DynamoDB using the AWS Database Encryption SDK. + + This step will be implemented in the future. It will demonstrate how to: + 1. Configure a DynamoDB client with full encryption capabilities + 2. Write fully encrypted items that cannot be read by plaintext clients (Step 0) + 3. Read both plaintext items and fully encrypted items + + Running this example requires access to the DDB Table whose name + is provided in the function parameter. + This table must be configured with the following + primary key configuration: + - Partition key is named "partition_key" with type (S) + - Sort key is named "sort_key" with type (S) + */ + public class MigrationStep2 + { + // This method will be implemented in the future + public static async Task MigrationStep2Example(string kmsKeyId, string ddbTableName, string partitionKeyValue, string sortKeyWriteValue, string sortKeyReadValue) + { + // TODO: Implement MigrationStep2 + throw new NotImplementedException("MigrationStep2 is not yet implemented"); + } + } +} diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3.cs new file mode 100644 index 000000000..c978eeddb --- /dev/null +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Amazon.DynamoDBv2; +using Amazon.DynamoDBv2.Model; +using System.Diagnostics; +using System.Net; +using AWS.Cryptography.DbEncryptionSDK.DynamoDb; +using AWS.Cryptography.DbEncryptionSDK.StructuredEncryption; +using AWS.Cryptography.MaterialProviders; +using Examples.migration.PlaintextToAWSDBE; + +namespace Examples.migration.PlaintextToAWSDBE.awsdbe +{ + /* + Migration Step 3: This is the final step in the migration process from + plaintext to encrypted DynamoDB using the AWS Database Encryption SDK. + + This step will be implemented in the future. It will demonstrate how to: + 1. Configure a DynamoDB client with full encryption capabilities + 2. Write fully encrypted items with additional security features + 3. Read both plaintext items and fully encrypted items + + Running this example requires access to the DDB Table whose name + is provided in the function parameter. + This table must be configured with the following + primary key configuration: + - Partition key is named "partition_key" with type (S) + - Sort key is named "sort_key" with type (S) + */ + public class MigrationStep3 + { + // This method will be implemented in the future + public static async Task MigrationStep3Example(string kmsKeyId, string ddbTableName, string partitionKeyValue, string sortKeyWriteValue, string sortKeyReadValue) + { + // TODO: Implement MigrationStep3 + throw new NotImplementedException("MigrationStep3 is not yet implemented"); + } + } +} diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0.cs new file mode 100644 index 000000000..215d9b028 --- /dev/null +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Amazon.DynamoDBv2; +using Amazon.DynamoDBv2.Model; +using System.Diagnostics; +using System.Net; +using Examples.migration.PlaintextToAWSDBE; + +namespace Examples.migration.PlaintextToAWSDBE.plaintext +{ + /* + Migration Step 0: This is the pre-migration step for the + plaintext-to-encrypted database migration, and is the starting + state for our migration from a plaintext database to a + client-side encrypted database encrypted using the + AWS Database Encryption SDK for DynamoDb. + + In this example, we configure a DynamoDbClient to + write a plaintext record to a table and read that record. + This emulates the starting state of a plaintext-to-encrypted + database migration; i.e. a plaintext database you can + read and write to with the DynamoDbClient. + + Running this example requires access to the DDB Table whose name + is provided in the function parameter. + This table must be configured with the following + primary key configuration: + - Partition key is named "partition_key" with type (S) + - Sort key is named "sort_key" with type (S) + */ + public class MigrationStep0 + { + public static async Task MigrationStep0Example(string ddbTableName, string partitionKeyValue, string sortKeyWriteValue, string sortKeyReadValue) + { + try + { + // 1. Create a standard DynamoDB client + var ddb = new AmazonDynamoDBClient(); + + // 2. Put an example item into DynamoDB table + // This item will be stored in plaintext. + string encryptedAndSignedValue = "this will be encrypted and signed"; + string signOnlyValue = "this will never be encrypted, but it will be signed"; + string doNothingValue = "this will never be encrypted nor signed"; + var item = new Dictionary + { + ["partition_key"] = new AttributeValue { S = partitionKeyValue }, + ["sort_key"] = new AttributeValue { N = sortKeyWriteValue }, + ["attribute1"] = new AttributeValue { S = encryptedAndSignedValue }, + ["attribute2"] = new AttributeValue { S = signOnlyValue }, + ["attribute3"] = new AttributeValue { S = doNothingValue } + }; + + var putRequest = new PutItemRequest + { + TableName = ddbTableName, + Item = item + }; + + var putResponse = await ddb.PutItemAsync(putRequest); + Debug.Assert(putResponse.HttpStatusCode == HttpStatusCode.OK); + + // 3. Get an item back from the table as it was written. + // If this is an item written in plaintext (i.e. any item written + // during Step 0 or 1), then the item will still be in plaintext + // and will be able to be processed. + // If this is an item that was encrypted client-side (i.e. any item written + // during Step 2 or after), then the item will still be encrypted client-side + // and will be unable to be processed in your code. To decrypt and process + // client-side encrypted items, you will need to configure encrypted reads on + // your dynamodb client (this is configured from Step 1 onwards). + var key = new Dictionary + { + ["partition_key"] = new AttributeValue { S = partitionKeyValue }, + ["sort_key"] = new AttributeValue { N = sortKeyReadValue } + }; + + var getRequest = new GetItemRequest + { + TableName = ddbTableName, + Key = key + }; + + var getResponse = await ddb.GetItemAsync(getRequest); + Debug.Assert(getResponse.HttpStatusCode == HttpStatusCode.OK); + + // 4. Verify we get the expected item back + if (getResponse.Item == null) + { + throw new Exception("No item found"); + } + + bool success = MigrationUtils.VerifyReturnedItem(getResponse, partitionKeyValue, sortKeyReadValue, encryptedAndSignedValue, signOnlyValue, doNothingValue); + if (success) + { + Console.WriteLine("MigrationStep0 completed successfully"); + } + return success; + } + catch (Exception e) + { + Console.WriteLine($"Error in MigrationStep0: {e.Message}"); + throw; + } + } + + } +} diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0Test.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0Test.cs new file mode 100644 index 000000000..5e23bf75f --- /dev/null +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0Test.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Amazon.DynamoDBv2; +using Amazon.DynamoDBv2.Model; +using System.Diagnostics; +using Xunit; +using Examples.migration.PlaintextToAWSDBE; + +namespace Examples.migration.PlaintextToAWSDBE.plaintext +{ + /* + Test for Migration Step 0: This tests the pre-migration step for the + plaintext-to-encrypted database migration. + + This test verifies that: + 1. Step 0 can successfully write and read plaintext items + 2. (Future) Step 0 can read items written by Step 1 (commented out until Step 1 is implemented) + 3. (Future) Step 0 cannot read items written by Steps 2 and 3 (commented out until Steps 2 and 3 are implemented) + + Running this test requires access to the DDB Table whose name + is provided by TestUtils.TEST_DDB_TABLE_NAME. + This table must be configured with the following + primary key configuration: + - Partition key is named "partition_key" with type (S) + - Sort key is named "sort_key" with type (S) + */ + public class MigrationStep0Test + { + [Fact] + public async Task TestMigrationStep0() + { + string kmsKeyID = TestUtils.TEST_KMS_KEY_ID; + string tableName = TestUtils.TEST_DDB_TABLE_NAME; + string partitionKey = Guid.NewGuid().ToString(); + string[] sortKeys = { "0", "1", "2", "3" }; + + try + { + // Successfully executes step 0 + bool success = await MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[0]); + Assert.True(success, "MigrationStep0 should complete successfully"); + + // The following tests will be implemented once the other migration steps are created + + // Given: Step 1 has succeeded + // await awsdbe.MigrationStep1.MigrationStep1Example(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[1]); + + // When: Execute Step 0 with sortReadValue=1, Then: Success (i.e. can read plaintext values) + // success = await MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[1]); + // Assert.True(success, "MigrationStep0 should be able to read items written by Step 1"); + + // Given: Step 2 has succeeded + // await awsdbe.MigrationStep2.MigrationStep2Example(kmsKeyID, tableName, partitionKey, sortKeys[2], sortKeys[2]); + + // When: Execute Step 0 with sortReadValue=2, Then: should error out when reading encrypted items. + // var exception = await Assert.ThrowsAsync(() => + // MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[2])); + // Assert.Contains("attribute1 is not a string attribute", exception.Message); + + // Given: Step 3 has succeeded (if it exists) + // await awsdbe.MigrationStep3.MigrationStep3Example(kmsKeyID, tableName, partitionKey, sortKeys[3], sortKeys[3]); + + // When: Execute Step 0 with sortReadValue=3, Then: should error out + // exception = await Assert.ThrowsAsync(() => + // MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[3])); + // Assert.Contains("attribute1 is not a string attribute", exception.Message); + } + finally + { + // Cleanup + await MigrationUtils.CleanupItems(tableName, partitionKey, sortKeys); + } + } + } +} From 04f569b492a2e1b164f6cd836090881417f73ac2 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Thu, 21 Aug 2025 22:09:36 -0700 Subject: [PATCH 02/15] auto commit --- .../runtimes/rust/examples/migration/mod.rs | 4 + .../plaintext_to_awsdbe/awsdbe/common.rs | 90 ++++++++ .../awsdbe/migration_step_1.rs | 136 +++++++++++ .../awsdbe/migration_step_2.rs | 138 +++++++++++ .../awsdbe/migration_step_3.rs | 139 +++++++++++ .../plaintext_to_awsdbe/awsdbe/mod.rs | 7 + .../plaintext_to_awsdbe/migration_tests.rs | 216 ++++++++++++++++++ .../plaintext_to_awsdbe/migration_utils.rs | 84 +++++++ .../migration/plaintext_to_awsdbe/mod.rs | 9 + .../plaintext/migration_step_0.rs | 111 +++++++++ .../plaintext_to_awsdbe/plaintext/mod.rs | 4 + 11 files changed, 938 insertions(+) create mode 100644 DynamoDbEncryption/runtimes/rust/examples/migration/mod.rs create mode 100644 DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/common.rs create mode 100644 DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_1.rs create mode 100644 DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_2.rs create mode 100644 DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_3.rs create mode 100644 DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/mod.rs create mode 100644 DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/migration_tests.rs create mode 100644 DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/migration_utils.rs create mode 100644 DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/mod.rs create mode 100644 DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/plaintext/migration_step_0.rs create mode 100644 DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/plaintext/mod.rs diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/mod.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/mod.rs new file mode 100644 index 000000000..7fa4b852c --- /dev/null +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/mod.rs @@ -0,0 +1,4 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +pub mod plaintext_to_awsdbe; diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/common.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/common.rs new file mode 100644 index 000000000..46a8e7acc --- /dev/null +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/common.rs @@ -0,0 +1,90 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use aws_db_esdk::material_providers::client; +use aws_db_esdk::material_providers::types::material_providers_config::MaterialProvidersConfig; +use aws_db_esdk::CryptoAction; +use aws_db_esdk::dynamodb::types::DynamoDbTableEncryptionConfig; +use aws_db_esdk::types::dynamo_db_tables_encryption_config::DynamoDbTablesEncryptionConfig; +use aws_db_esdk::PlaintextOverride; +use std::collections::HashMap; + +pub async fn create_table_configs( + kms_key_id: &str, + ddb_table_name: &str, + plaintext_override: PlaintextOverride, +) -> Result> { + // Create a Keyring. This Keyring will be responsible for protecting the data keys that protect your data. + // For this example, we will create a AWS KMS Keyring with the AWS KMS Key we want to use. + // We will use the `CreateMrkMultiKeyring` method to create this keyring, + // as it will correctly handle both single region and Multi-Region KMS Keys. + let provider_config = MaterialProvidersConfig::builder().build()?; + let mat_prov = client::Client::from_conf(provider_config)?; + let kms_keyring = mat_prov + .create_aws_kms_mrk_multi_keyring() + .generator(kms_key_id) + .send() + .await?; + + // Configure which attributes are encrypted and/or signed when writing new items. + // For each attribute that may exist on the items we plan to write to our DynamoDbTable, + // we must explicitly configure how they should be treated during item encryption: + // - ENCRYPT_AND_SIGN: The attribute is encrypted and included in the signature + // - SIGN_ONLY: The attribute not encrypted, but is still included in the signature + // - DO_NOTHING: The attribute is not encrypted and not included in the signature + let partition_key_name = "partition_key"; + let sort_key_name = "sort_key"; + let attribute_actions_on_encrypt = HashMap::from([ + (partition_key_name.to_string(), CryptoAction::SignOnly), + (sort_key_name.to_string(), CryptoAction::SignOnly), + ("attribute1".to_string(), CryptoAction::EncryptAndSign), + ("attribute2".to_string(), CryptoAction::SignOnly), + ("attribute3".to_string(), CryptoAction::DoNothing), + ]); + + // Configure which attributes we expect to be excluded in the signature + // when reading items. There are two options for configuring this: + // + // - (Recommended) Configure `allowedUnsignedAttributesPrefix`: + // When defining your DynamoDb schema and deciding on attribute names, + // choose a distinguishing prefix (such as ":") for all attributes that + // you do not want to include in the signature. + // This has two main benefits: + // - It is easier to reason about the security and authenticity of data within your item + // when all unauthenticated data is easily distinguishable by their attribute name. + // - If you need to add new unauthenticated attributes in the future, + // you can easily make the corresponding update to your `attributeActionsOnEncrypt` + // and immediately start writing to that new attribute, without + // any other configuration update needed. + // Once you configure this field, it is not safe to update it. + // + // - Configure `allowedUnsignedAttributes`: You may also explicitly list + // a set of attributes that should be considered unauthenticated when encountered + // on read. Be careful if you use this configuration. Do not remove an attribute + // name from this configuration, even if you are no longer writing with that attribute, + // as old items may still include this attribute, and our configuration needs to know + // to continue to exclude this attribute from the signature scope. + // If you add new attribute names to this field, you must first deploy the update to this + // field to all readers in your host fleet before deploying the update to start writing + // with that new attribute. + // + // For this example, we will explicitly list the attributes that are not signed. + let unsigned_attributes = vec!["attribute3".to_string()]; + + // Create the DynamoDb Encryption configuration for the table we will be writing to. + let table_config = DynamoDbTableEncryptionConfig::builder() + .logical_table_name(ddb_table_name) + .partition_key_name(partition_key_name) + .sort_key_name(sort_key_name) + .attribute_actions_on_encrypt(attribute_actions_on_encrypt) + .keyring(kms_keyring) + .allowed_unsigned_attributes(unsigned_attributes) + .plaintext_override(plaintext_override) + .build()?; + + let table_configs = DynamoDbTablesEncryptionConfig::builder() + .table_encryption_configs(HashMap::from([(ddb_table_name.to_string(), table_config)])) + .build()?; + + Ok(table_configs) +} diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_1.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_1.rs new file mode 100644 index 000000000..c8377b0d6 --- /dev/null +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_1.rs @@ -0,0 +1,136 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use aws_sdk_dynamodb::types::AttributeValue; +use std::collections::HashMap; +use aws_db_esdk::intercept::DbEsdkInterceptor; +use aws_db_esdk::PlaintextOverride; +use crate::migration::plaintext_to_awsdbe::migration_utils::{ + verify_returned_item, ENCRYPTED_AND_SIGNED_VALUE, SIGN_ONLY_VALUE, DO_NOTHING_VALUE, +}; +use crate::migration::plaintext_to_awsdbe::awsdbe::common::create_table_configs; + +/* +Migration Step 1: This is the first step in the migration process from +plaintext to encrypted DynamoDB using the AWS Database Encryption SDK. + +In this example, we configure a DynamoDB Encryption client to do the following: +1. Write items only in plaintext +2. Read items in plaintext or, if the item is encrypted, decrypt with our encryption configuration + +While this step configures your client to be ready to start reading encrypted items, +we do not yet expect to be reading any encrypted items, +as our client still writes plaintext items. +Before you move on to step 2, ensure that these changes have successfully been deployed +to all of your readers. + +Running this example requires access to the DDB Table whose name +is provided in the function parameter. +This table must be configured with the following +primary key configuration: + - Partition key is named "partition_key" with type (S) + - Sort key is named "sort_key" with type (N) +*/ +pub async fn migration_step_1_example( + kms_key_id: &str, + ddb_table_name: &str, + partition_key_value: &str, + sort_key_write_value: &str, + sort_key_read_value: &str, +) -> Result> { + // 1. Create table configurations + // In this step of migration we will use PlaintextOverride::ForcePlaintextWriteAllowPlaintextRead + // which means: + // - Write: Items are forced to be written as plaintext. + // Items may not be written as encrypted items. + // - Read: Items are allowed to be read as plaintext. + // Items are allowed to be read as encrypted items. + let table_configs = create_table_configs( + kms_key_id, + ddb_table_name, + PlaintextOverride::ForcePlaintextWriteAllowPlaintextRead, + ) + .await?; + + // 2. Create a new AWS SDK DynamoDb client using the TableEncryptionConfigs + let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; + let dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config) + .interceptor(DbEsdkInterceptor::new(table_configs)?) + .build(); + let ddb = aws_sdk_dynamodb::Client::from_conf(dynamo_config); + + // 3. Put an item into our table using the above client. + // This item will be stored in plaintext due to our PlaintextOverride configuration. + let partition_key_name = "partition_key"; + let sort_key_name = "sort_key"; + let encrypted_and_signed_value = ENCRYPTED_AND_SIGNED_VALUE; + let sign_only_value = SIGN_ONLY_VALUE; + let do_nothing_value = DO_NOTHING_VALUE; + let item = HashMap::from([ + ( + partition_key_name.to_string(), + AttributeValue::S(partition_key_value.to_string()), + ), + ( + sort_key_name.to_string(), + AttributeValue::N(sort_key_write_value.to_string()), + ), + ( + "attribute1".to_string(), + AttributeValue::S(encrypted_and_signed_value.to_string()), + ), + ( + "attribute2".to_string(), + AttributeValue::S(sign_only_value.to_string()), + ), + ( + "attribute3".to_string(), + AttributeValue::S(do_nothing_value.to_string()), + ), + ]); + + ddb.put_item() + .table_name(ddb_table_name) + .set_item(Some(item)) + .send() + .await?; + + // 4. Get an item back from the table using the same client. + // If this is an item written in plaintext (i.e. any item written + // during Step 0 or 1), then the item will still be in plaintext. + // If this is an item that was encrypted client-side (i.e. any item written + // during Step 2 or after), then the item will be decrypted client-side + // and surfaced as a plaintext item. + let key = HashMap::from([ + ( + partition_key_name.to_string(), + AttributeValue::S(partition_key_value.to_string()), + ), + ( + sort_key_name.to_string(), + AttributeValue::N(sort_key_read_value.to_string()), + ), + ]); + + let response = ddb + .get_item() + .table_name(ddb_table_name) + .set_key(Some(key)) + // In this example we configure a strongly consistent read + // because we perform a read immediately after a write (for demonstrative purposes). + // By default, reads are only eventually consistent. + .consistent_read(true) + .send() + .await?; + + // 5. Verify we get the expected item back + if let Some(item) = response.item { + let success = verify_returned_item(&item, partition_key_value, sort_key_read_value)?; + if success { + println!("MigrationStep1 completed successfully"); + } + Ok(success) + } else { + Err("No item found".into()) + } +} diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_2.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_2.rs new file mode 100644 index 000000000..e780aa8ec --- /dev/null +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_2.rs @@ -0,0 +1,138 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use aws_sdk_dynamodb::types::AttributeValue; +use std::collections::HashMap; +use aws_db_esdk::intercept::DbEsdkInterceptor; +use aws_db_esdk::PlaintextOverride; +use crate::migration::plaintext_to_awsdbe::migration_utils::{ + verify_returned_item, ENCRYPTED_AND_SIGNED_VALUE, SIGN_ONLY_VALUE, DO_NOTHING_VALUE, +}; +use crate::migration::plaintext_to_awsdbe::awsdbe::common::create_table_configs; + +/* +Migration Step 2: This is the second step in the migration process from +plaintext to encrypted DynamoDB using the AWS Database Encryption SDK. + +In this example, we configure a DynamoDB Encryption client to do the following: +1. Write items with encryption (no longer writing plaintext) +2. Read both plaintext items and encrypted items + +Once you deploy this change to your system, you will have a dataset +containing both encrypted and plaintext items. +Because the changes in Step 1 have been deployed to all readers, +we can be sure that our entire system is ready to read this new data. + +Before you move onto the next step, you will need to encrypt all plaintext items in your dataset. +How you will want to do this depends on your system. + +Running this example requires access to the DDB Table whose name +is provided in the function parameter. +This table must be configured with the following +primary key configuration: + - Partition key is named "partition_key" with type (S) + - Sort key is named "sort_key" with type (N) +*/ +pub async fn migration_step_2_example( + kms_key_id: &str, + ddb_table_name: &str, + partition_key_value: &str, + sort_key_write_value: &str, + sort_key_read_value: &str, +) -> Result> { + // 1. Create table configurations + // In this step of migration we will use PlaintextOverride::ForbidPlaintextWriteAllowPlaintextRead + // which means: + // - Write: Items are forbidden to be written as plaintext. + // Items will be written as encrypted items. + // - Read: Items are allowed to be read as plaintext. + // Items are allowed to be read as encrypted items. + let table_configs = create_table_configs( + kms_key_id, + ddb_table_name, + PlaintextOverride::ForbidPlaintextWriteAllowPlaintextRead, + ) + .await?; + + // 2. Create a new AWS SDK DynamoDb client using the TableEncryptionConfigs + let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; + let dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config) + .interceptor(DbEsdkInterceptor::new(table_configs)?) + .build(); + let ddb = aws_sdk_dynamodb::Client::from_conf(dynamo_config); + + // 3. Put an item into our table using the above client. + // This item will be encrypted due to our PlaintextOverride configuration. + let partition_key_name = "partition_key"; + let sort_key_name = "sort_key"; + let encrypted_and_signed_value = ENCRYPTED_AND_SIGNED_VALUE; + let sign_only_value = SIGN_ONLY_VALUE; + let do_nothing_value = DO_NOTHING_VALUE; + let item = HashMap::from([ + ( + partition_key_name.to_string(), + AttributeValue::S(partition_key_value.to_string()), + ), + ( + sort_key_name.to_string(), + AttributeValue::N(sort_key_write_value.to_string()), + ), + ( + "attribute1".to_string(), + AttributeValue::S(encrypted_and_signed_value.to_string()), + ), + ( + "attribute2".to_string(), + AttributeValue::S(sign_only_value.to_string()), + ), + ( + "attribute3".to_string(), + AttributeValue::S(do_nothing_value.to_string()), + ), + ]); + + ddb.put_item() + .table_name(ddb_table_name) + .set_item(Some(item)) + .send() + .await?; + + // 4. Get an item back from the table using the same client. + // If this is an item written in plaintext (i.e. any item written + // during Step 0 or 1), then the item will still be in plaintext. + // If this is an item that was encrypted client-side (i.e. any item written + // during Step 2 or after), then the item will be decrypted client-side + // and surfaced as a plaintext item. + let key = HashMap::from([ + ( + partition_key_name.to_string(), + AttributeValue::S(partition_key_value.to_string()), + ), + ( + sort_key_name.to_string(), + AttributeValue::N(sort_key_read_value.to_string()), + ), + ]); + + let response = ddb + .get_item() + .table_name(ddb_table_name) + .set_key(Some(key)) + // In this example we configure a strongly consistent read + // because we perform a read immediately after a write (for demonstrative purposes). + // By default, reads are only eventually consistent. + .consistent_read(true) + .send() + .await?; + + // 5. Verify we get the expected item back + if let Some(item) = response.item { + let success = verify_returned_item(&item, partition_key_value, sort_key_read_value)?; + if success { + println!("MigrationStep2 completed successfully"); + } + Ok(success) + } else { + Err("No item found".into()) + } +} diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_3.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_3.rs new file mode 100644 index 000000000..577fbd441 --- /dev/null +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_3.rs @@ -0,0 +1,139 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use aws_sdk_dynamodb::types::AttributeValue; +use std::collections::HashMap; +use aws_db_esdk::intercept::DbEsdkInterceptor; +use aws_db_esdk::PlaintextOverride; +use crate::migration::plaintext_to_awsdbe::migration_utils::{ + verify_returned_item, ENCRYPTED_AND_SIGNED_VALUE, SIGN_ONLY_VALUE, DO_NOTHING_VALUE, +}; +use crate::migration::plaintext_to_awsdbe::awsdbe::common::create_table_configs; + +/* +Migration Step 3: This is the final step in the migration process from +plaintext to encrypted DynamoDB using the AWS Database Encryption SDK. + +In this example, we configure a DynamoDB Encryption client to do the following: +1. Write items with encryption (no longer writing plaintext) +2. Read only encrypted items (no longer reading plaintext) + +Once you complete Step 3, all items being read by your system are encrypted. + +Before you move onto this step, you will need to encrypt all plaintext items in your dataset. +How you will want to do this depends on your system. + +Running this example requires access to the DDB Table whose name +is provided in the function parameter. +This table must be configured with the following +primary key configuration: + - Partition key is named "partition_key" with type (S) + - Sort key is named "sort_key" with type (N) +*/ +pub async fn migration_step_3_example( + kms_key_id: &str, + ddb_table_name: &str, + partition_key_value: &str, + sort_key_write_value: &str, + sort_key_read_value: &str, +) -> Result> { + // 1. Create table configurations + // In this step of migration we will use PlaintextOverride::ForbidPlaintextWriteForbidPlaintextRead + // which means: + // - Write: Items are forbidden to be written as plaintext. + // Items will be written as encrypted items. + // - Read: Items are forbidden to be read as plaintext. + // Items will be read as encrypted items. + // Note: If you do not specify a PlaintextOverride, it defaults to + // ForbidPlaintextWriteForbidPlaintextRead, which is the desired + // behavior for a client interacting with a fully encrypted database. + let table_configs = create_table_configs( + kms_key_id, + ddb_table_name, + PlaintextOverride::ForbidPlaintextWriteForbidPlaintextRead, + ) + .await?; + + // 2. Create a new AWS SDK DynamoDb client using the TableEncryptionConfigs + let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; + let dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config) + .interceptor(DbEsdkInterceptor::new(table_configs)?) + .build(); + let ddb = aws_sdk_dynamodb::Client::from_conf(dynamo_config); + + // 3. Put an item into our table using the above client. + // This item will be encrypted due to our PlaintextOverride configuration. + let partition_key_name = "partition_key"; + let sort_key_name = "sort_key"; + let encrypted_and_signed_value = ENCRYPTED_AND_SIGNED_VALUE; + let sign_only_value = SIGN_ONLY_VALUE; + let do_nothing_value = DO_NOTHING_VALUE; + let item = HashMap::from([ + ( + "partition_key".to_string(), + AttributeValue::S(partition_key_value.to_string()), + ), + ( + "sort_key".to_string(), + AttributeValue::N(sort_key_write_value.to_string()), + ), + ( + "attribute1".to_string(), + AttributeValue::S(encrypted_and_signed_value.to_string()), + ), + ( + "attribute2".to_string(), + AttributeValue::S(sign_only_value.to_string()), + ), + ( + "attribute3".to_string(), + AttributeValue::S(do_nothing_value.to_string()), + ), + ]); + + ddb.put_item() + .table_name(ddb_table_name) + .set_item(Some(item)) + .send() + .await?; + + // 4. Get an item back from the table using the same client. + // If this is an item written in plaintext (i.e. any item written + // during Step 0 or 1), then the read will fail, as we have + // configured our client to forbid reading plaintext items. + // If this is an item that was encrypted client-side (i.e. any item written + // during Step 2 or after), then the item will be decrypted client-side + // and surfaced as a plaintext item. + let key = HashMap::from([ + ( + "partition_key".to_string(), + AttributeValue::S(partition_key_value.to_string()), + ), + ( + "sort_key".to_string(), + AttributeValue::N(sort_key_read_value.to_string()), + ), + ]); + + let response = ddb + .get_item() + .table_name(ddb_table_name) + .set_key(Some(key)) + // In this example we configure a strongly consistent read + // because we perform a read immediately after a write (for demonstrative purposes). + // By default, reads are only eventually consistent. + .consistent_read(true) + .send() + .await?; + + // Verify we get the expected item back + if let Some(item) = response.item { + let success = verify_returned_item(&item, partition_key_value, sort_key_read_value)?; + if success { + println!("MigrationStep3 completed successfully"); + } + Ok(success) + } else { + Err("No item found".into()) + } +} diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/mod.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/mod.rs new file mode 100644 index 000000000..6a3380ee2 --- /dev/null +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/mod.rs @@ -0,0 +1,7 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +pub mod common; +pub mod migration_step_1; +pub mod migration_step_2; +pub mod migration_step_3; diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/migration_tests.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/migration_tests.rs new file mode 100644 index 000000000..d6725a73a --- /dev/null +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/migration_tests.rs @@ -0,0 +1,216 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use crate::test_utils; +use uuid::Uuid; + +use crate::migration::plaintext_to_awsdbe::plaintext::migration_step_0::migration_step_0_example; +use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_1::migration_step_1_example; +use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_2::migration_step_2_example; +use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_3::migration_step_3_example; + +// Helper method to clean up test items +async fn cleanup_items( + table_name: &str, + partition_key: &str, + sort_key: &str, +) -> Result<(), Box> { + let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; + let ddb = aws_sdk_dynamodb::Client::new(&sdk_config); + + let key = std::collections::HashMap::from([ + ( + "partition_key".to_string(), + aws_sdk_dynamodb::types::AttributeValue::S(partition_key.to_string()), + ), + ( + "sort_key".to_string(), + aws_sdk_dynamodb::types::AttributeValue::N(sort_key.to_string()), + ), + ]); + + ddb.delete_item() + .table_name(table_name) + .set_key(Some(key)) + .send() + .await?; + + Ok(()) +} + +#[tokio::test] +async fn test_migration_step_0() -> Result<(), Box> { + let kms_key_id = test_utils::TEST_KMS_KEY_ID; + let table_name = test_utils::TEST_DDB_TABLE_NAME; + let partition_key = Uuid::new_v4().to_string(); + let sort_keys = ["0", "1", "2", "3"]; + + // Successfully executes step 0 + let success = migration_step_0_example(table_name, &partition_key, sort_keys[0], sort_keys[0]).await?; + assert!(success, "MigrationStep0 should complete successfully"); + + // Given: Step 1 has succeeded + migration_step_1_example(kms_key_id, table_name, &partition_key, sort_keys[1], sort_keys[1]).await?; + + // When: Execute Step 0 with sortReadValue=1, Then: Success (i.e. can read plaintext values) + let success = migration_step_0_example(table_name, &partition_key, sort_keys[0], sort_keys[1]).await?; + assert!(success, "MigrationStep0 should be able to read items written by Step 1"); + + // Given: Step 2 has succeeded + migration_step_2_example(kms_key_id, table_name, &partition_key, sort_keys[2], sort_keys[2]).await?; + + // When: Execute Step 0 with sortReadValue=2, Then: should error out when reading encrypted items. + let result = migration_step_0_example(table_name, &partition_key, sort_keys[0], sort_keys[2]).await; + assert!(result.is_err(), "MigrationStep0 should fail when reading encrypted items"); + assert!(result.unwrap_err().to_string().contains("attribute1 mismatch")); + + // Given: Step 3 has succeeded + migration_step_3_example(kms_key_id, table_name, &partition_key, sort_keys[3], sort_keys[3]).await?; + + // When: Execute Step 0 with sortReadValue=3, Then: should error out + let result = migration_step_0_example(table_name, &partition_key, sort_keys[0], sort_keys[3]).await; + assert!(result.is_err(), "MigrationStep0 should fail when reading encrypted items"); + assert!(result.unwrap_err().to_string().contains("attribute1 mismatch")); + + // Cleanup + for sort_key in &sort_keys { + cleanup_items(table_name, &partition_key, sort_key).await?; + } + + Ok(()) +} + +#[tokio::test] +async fn test_migration_step_1() -> Result<(), Box> { + let kms_key_id = test_utils::TEST_KMS_KEY_ID; + let table_name = test_utils::TEST_DDB_TABLE_NAME; + let partition_key = Uuid::new_v4().to_string(); + let sort_keys = ["0", "1", "2", "3"]; + + // Given: Step 0 has succeeded + let success = migration_step_0_example(table_name, &partition_key, sort_keys[0], sort_keys[0]).await?; + assert!(success, "MigrationStep0 should complete successfully"); + + // Successfully executes step 1 + let success = migration_step_1_example(kms_key_id, table_name, &partition_key, sort_keys[1], sort_keys[1]).await?; + assert!(success, "MigrationStep1 should complete successfully"); + + // When: Execute Step 1 with sortReadValue=0, Then: Success (i.e. can read plaintext values from Step 0) + let success = migration_step_1_example(kms_key_id, table_name, &partition_key, sort_keys[1], sort_keys[0]).await?; + assert!(success, "MigrationStep1 should be able to read items written by Step 0"); + + // Given: Step 2 has succeeded + let success = migration_step_2_example(kms_key_id, table_name, &partition_key, sort_keys[2], sort_keys[2]).await?; + assert!(success, "MigrationStep2 should complete successfully"); + + // When: Execute Step 1 with sortReadValue=2, Then: Success (i.e. can read encrypted values from Step 2) + let success = migration_step_1_example(kms_key_id, table_name, &partition_key, sort_keys[1], sort_keys[2]).await?; + assert!(success, "MigrationStep1 should be able to read items written by Step 2"); + + // Given: Step 3 has succeeded + let success = migration_step_3_example(kms_key_id, table_name, &partition_key, sort_keys[3], sort_keys[3]).await?; + assert!(success, "MigrationStep3 should complete successfully"); + + // When: Execute Step 1 with sortReadValue=3, Then: Success (i.e. can read encrypted values from Step 3) + let success = migration_step_1_example(kms_key_id, table_name, &partition_key, sort_keys[1], sort_keys[3]).await?; + assert!(success, "MigrationStep1 should be able to read items written by Step 3"); + + // Cleanup + for sort_key in &sort_keys { + cleanup_items(table_name, &partition_key, sort_key).await?; + } + + Ok(()) +} + +#[tokio::test] +async fn test_migration_step_2() -> Result<(), Box> { + let kms_key_id = test_utils::TEST_KMS_KEY_ID; + let table_name = test_utils::TEST_DDB_TABLE_NAME; + let partition_key = Uuid::new_v4().to_string(); + let sort_keys = ["0", "1", "2", "3"]; + + // Given: Step 0 has succeeded + let success = migration_step_0_example(table_name, &partition_key, sort_keys[0], sort_keys[0]).await?; + assert!(success, "MigrationStep0 should complete successfully"); + + // Given: Step 1 has succeeded + let success = migration_step_1_example(kms_key_id, table_name, &partition_key, sort_keys[1], sort_keys[1]).await?; + assert!(success, "MigrationStep1 should complete successfully"); + + // Successfully executes step 2 + let success = migration_step_2_example(kms_key_id, table_name, &partition_key, sort_keys[2], sort_keys[2]).await?; + assert!(success, "MigrationStep2 should complete successfully"); + + // When: Execute Step 2 with sortReadValue=0, Then: Success (i.e. can read plaintext values from Step 0) + let success = migration_step_2_example(kms_key_id, table_name, &partition_key, sort_keys[2], sort_keys[0]).await?; + assert!(success, "MigrationStep2 should be able to read items written by Step 0"); + + // When: Execute Step 2 with sortReadValue=1, Then: Success (i.e. can read plaintext values from Step 1) + let success = migration_step_2_example(kms_key_id, table_name, &partition_key, sort_keys[2], sort_keys[1]).await?; + assert!(success, "MigrationStep2 should be able to read items written by Step 1"); + + // Given: Step 3 has succeeded + let success = migration_step_3_example(kms_key_id, table_name, &partition_key, sort_keys[3], sort_keys[3]).await?; + assert!(success, "MigrationStep3 should complete successfully"); + + // When: Execute Step 2 with sortReadValue=3, Then: Success (i.e. can read encrypted values from Step 3) + let success = migration_step_2_example(kms_key_id, table_name, &partition_key, sort_keys[2], sort_keys[3]).await?; + assert!(success, "MigrationStep2 should be able to read items written by Step 3"); + + // Cleanup + for sort_key in &sort_keys { + cleanup_items(table_name, &partition_key, sort_key).await?; + } + + Ok(()) +} + +#[tokio::test] +async fn test_migration_step_3() -> Result<(), Box> { + let kms_key_id = test_utils::TEST_KMS_KEY_ID; + let table_name = test_utils::TEST_DDB_TABLE_NAME; + let partition_key = Uuid::new_v4().to_string(); + let sort_keys = ["0", "1", "2", "3"]; + + // Given: Step 0 has succeeded + let success = migration_step_0_example(table_name, &partition_key, sort_keys[0], sort_keys[0]).await?; + assert!(success, "MigrationStep0 should complete successfully"); + + // Given: Step 1 has succeeded + let success = migration_step_1_example(kms_key_id, table_name, &partition_key, sort_keys[1], sort_keys[1]).await?; + assert!(success, "MigrationStep1 should complete successfully"); + + // Given: Step 2 has succeeded + let success = migration_step_2_example(kms_key_id, table_name, &partition_key, sort_keys[2], sort_keys[2]).await?; + assert!(success, "MigrationStep2 should complete successfully"); + + // Successfully executes step 3 + let success = migration_step_3_example(kms_key_id, table_name, &partition_key, sort_keys[3], sort_keys[3]).await?; + assert!(success, "MigrationStep3 should complete successfully"); + + // When: Execute Step 3 with sortReadValue=0, Then: should error out when reading plaintext items from Step 0 + let result = migration_step_3_example(kms_key_id, table_name, &partition_key, sort_keys[3], sort_keys[0]).await; + assert!(result.is_err(), "MigrationStep3 should fail when reading plaintext items"); + let error_msg = result.unwrap_err().to_string().to_lowercase(); + assert!(error_msg.contains("encrypted item missing expected header and footer attributes") || + error_msg.contains("header") || error_msg.contains("footer")); + + // When: Execute Step 3 with sortReadValue=1, Then: should error out when reading plaintext items from Step 1 + let result = migration_step_3_example(kms_key_id, table_name, &partition_key, sort_keys[3], sort_keys[1]).await; + assert!(result.is_err(), "MigrationStep3 should fail when reading plaintext items"); + let error_msg = result.unwrap_err().to_string().to_lowercase(); + assert!(error_msg.contains("encrypted item missing expected header and footer attributes") || + error_msg.contains("header") || error_msg.contains("footer")); + + // When: Execute Step 3 with sortReadValue=2, Then: Success (i.e. can read encrypted values from Step 2) + let success = migration_step_3_example(kms_key_id, table_name, &partition_key, sort_keys[3], sort_keys[2]).await?; + assert!(success, "MigrationStep3 should be able to read items written by Step 2"); + + // Cleanup + for sort_key in &sort_keys { + cleanup_items(table_name, &partition_key, sort_key).await?; + } + + Ok(()) +} diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/migration_utils.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/migration_utils.rs new file mode 100644 index 000000000..83d76f248 --- /dev/null +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/migration_utils.rs @@ -0,0 +1,84 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use aws_sdk_dynamodb::types::AttributeValue; +use std::collections::HashMap; + +/* +Utility module for the PlaintextToAWSDBE migration examples. +This module contains shared functionality used by all migration steps. +*/ + +// Common attribute values used across all migration steps +pub const ENCRYPTED_AND_SIGNED_VALUE: &str = "this will be encrypted and signed"; +pub const SIGN_ONLY_VALUE: &str = "this will never be encrypted, but it will be signed"; +pub const DO_NOTHING_VALUE: &str = "this will never be encrypted nor signed"; + +// Verify that a returned item matches the expected values +pub fn verify_returned_item( + item: &HashMap, + partition_key_value: &str, + sort_key_value: &str, +) -> Result> { + if let Some(AttributeValue::S(pk)) = item.get("partition_key") { + if pk != partition_key_value { + return Err(format!( + "partition_key mismatch: expected {}, got {}", + partition_key_value, pk + ) + .into()); + } + } else { + return Err("partition_key not found or not a string".into()); + } + + if let Some(AttributeValue::N(sk)) = item.get("sort_key") { + if sk != sort_key_value { + return Err(format!( + "sort_key mismatch: expected {}, got {}", + sort_key_value, sk + ) + .into()); + } + } else { + return Err("sort_key not found or not a number".into()); + } + + if let Some(AttributeValue::S(attr1)) = item.get("attribute1") { + if attr1 != ENCRYPTED_AND_SIGNED_VALUE { + return Err(format!( + "attribute1 mismatch: expected {}, got {}", + ENCRYPTED_AND_SIGNED_VALUE, attr1 + ) + .into()); + } + } else { + return Err("attribute1 not found or not a string".into()); + } + + if let Some(AttributeValue::S(attr2)) = item.get("attribute2") { + if attr2 != SIGN_ONLY_VALUE { + return Err(format!( + "attribute2 mismatch: expected {}, got {}", + SIGN_ONLY_VALUE, attr2 + ) + .into()); + } + } else { + return Err("attribute2 not found or not a string".into()); + } + + if let Some(AttributeValue::S(attr3)) = item.get("attribute3") { + if attr3 != DO_NOTHING_VALUE { + return Err(format!( + "attribute3 mismatch: expected {}, got {}", + DO_NOTHING_VALUE, attr3 + ) + .into()); + } + } else { + return Err("attribute3 not found or not a string".into()); + } + + Ok(true) +} diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/mod.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/mod.rs new file mode 100644 index 000000000..4706cd989 --- /dev/null +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/mod.rs @@ -0,0 +1,9 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +pub mod migration_utils; +pub mod plaintext; +pub mod awsdbe; + +#[cfg(test)] +pub mod migration_tests; diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/plaintext/migration_step_0.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/plaintext/migration_step_0.rs new file mode 100644 index 000000000..f4d341751 --- /dev/null +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/plaintext/migration_step_0.rs @@ -0,0 +1,111 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use aws_sdk_dynamodb::types::AttributeValue; +use std::collections::HashMap; +use crate::migration::plaintext_to_awsdbe::migration_utils::{ + verify_returned_item, ENCRYPTED_AND_SIGNED_VALUE, SIGN_ONLY_VALUE, DO_NOTHING_VALUE, +}; + +/* +Migration Step 0: This is the pre-migration step for the +plaintext-to-encrypted database migration, and is the starting +state for our migration from a plaintext database to a +client-side encrypted database encrypted using the +AWS Database Encryption SDK for DynamoDb. + +In this example, we configure a DynamoDbClient to +write a plaintext record to a table and read that record. +This emulates the starting state of a plaintext-to-encrypted +database migration; i.e. a plaintext database you can +read and write to with the DynamoDbClient. + +Running this example requires access to the DDB Table whose name +is provided in the function parameter. +This table must be configured with the following +primary key configuration: + - Partition key is named "partition_key" with type (S) + - Sort key is named "sort_key" with type (N) +*/ +pub async fn migration_step_0_example( + ddb_table_name: &str, + partition_key_value: &str, + sort_key_write_value: &str, + sort_key_read_value: &str, +) -> Result> { + // 1. Create a standard DynamoDB client + let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; + let ddb = aws_sdk_dynamodb::Client::new(&sdk_config); + + // 2. Put an example item into DynamoDB table + // This item will be stored in plaintext. + let encrypted_and_signed_value = ENCRYPTED_AND_SIGNED_VALUE; + let sign_only_value = SIGN_ONLY_VALUE; + let do_nothing_value = DO_NOTHING_VALUE; + let item = HashMap::from([ + ( + "partition_key".to_string(), + AttributeValue::S(partition_key_value.to_string()), + ), + ( + "sort_key".to_string(), + AttributeValue::N(sort_key_write_value.to_string()), + ), + ( + "attribute1".to_string(), + AttributeValue::S(encrypted_and_signed_value.to_string()), + ), + ( + "attribute2".to_string(), + AttributeValue::S(sign_only_value.to_string()), + ), + ( + "attribute3".to_string(), + AttributeValue::S(do_nothing_value.to_string()), + ), + ]); + + ddb.put_item() + .table_name(ddb_table_name) + .set_item(Some(item)) + .send() + .await?; + + // 3. Get an item back from the table as it was written. + // If this is an item written in plaintext (i.e. any item written + // during Step 0 or 1), then the item will still be in plaintext + // and will be able to be processed. + // If this is an item that was encrypted client-side (i.e. any item written + // during Step 2 or after), then the item will still be encrypted client-side + // and will be unable to be processed in your code. To decrypt and process + // client-side encrypted items, you will need to configure encrypted reads on + // your dynamodb client (this is configured from Step 1 onwards). + let key = HashMap::from([ + ( + "partition_key".to_string(), + AttributeValue::S(partition_key_value.to_string()), + ), + ( + "sort_key".to_string(), + AttributeValue::N(sort_key_read_value.to_string()), + ), + ]); + + let response = ddb + .get_item() + .table_name(ddb_table_name) + .set_key(Some(key)) + .send() + .await?; + + // 4. Verify we get the expected item back + if let Some(item) = response.item { + let success = verify_returned_item(&item, partition_key_value, sort_key_read_value)?; + if success { + println!("MigrationStep0 completed successfully"); + } + Ok(success) + } else { + Err("No item found".into()) + } +} diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/plaintext/mod.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/plaintext/mod.rs new file mode 100644 index 000000000..c1d709e7b --- /dev/null +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/plaintext/mod.rs @@ -0,0 +1,4 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +pub mod migration_step_0; From 969e4e402b47467b416180e23a44586daf0de231 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Mon, 25 Aug 2025 15:08:47 -0700 Subject: [PATCH 03/15] remove net --- .../PlaintextToAWSDBE/MigrationUtils.cs | 83 ------------- .../awsdbe/MigrationStep1.cs | 40 ------- .../awsdbe/MigrationStep2.cs | 40 ------- .../awsdbe/MigrationStep3.cs | 40 ------- .../plaintext/MigrationStep0.cs | 109 ------------------ .../plaintext/MigrationStep0Test.cs | 76 ------------ 6 files changed, 388 deletions(-) delete mode 100644 Examples/runtimes/net/src/migration/PlaintextToAWSDBE/MigrationUtils.cs delete mode 100644 Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs delete mode 100644 Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2.cs delete mode 100644 Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3.cs delete mode 100644 Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0.cs delete mode 100644 Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0Test.cs diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/MigrationUtils.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/MigrationUtils.cs deleted file mode 100644 index 4fb02afc7..000000000 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/MigrationUtils.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using System.Collections.Generic; -using Amazon.DynamoDBv2.Model; - -namespace Examples.migration.PlaintextToAWSDBE -{ - /* - Utility class for the PlaintextToAWSDBE migration examples. - This class contains shared functionality used by all migration steps. - */ - public class MigrationUtils - { - // Verify that a returned item matches the expected values - public static bool VerifyReturnedItem(GetItemResponse response, string partitionKeyValue, string sortKeyValue, - string encryptedAndSignedValue, string signOnlyValue, string doNothingValue) - { - var item = response.Item; - - // Verify partition key - if (!item.ContainsKey("partition_key") || item["partition_key"].S != partitionKeyValue) - { - throw new Exception($"partition_key mismatch: expected {partitionKeyValue}, got {(item.ContainsKey("partition_key") ? item["partition_key"].S : "null")}"); - } - - // Verify sort key - if (!item.ContainsKey("sort_key") || item["sort_key"].N != sortKeyValue) - { - throw new Exception($"sort_key mismatch: expected {sortKeyValue}, got {(item.ContainsKey("sort_key") ? item["sort_key"].N : "null")}"); - } - - // Verify attribute1 (will be encrypted and signed in future steps) - if (!item.ContainsKey("attribute1") || item["attribute1"].S != encryptedAndSignedValue) - { - throw new Exception($"attribute1 mismatch: expected {encryptedAndSignedValue}, got {(item.ContainsKey("attribute1") ? item["attribute1"].S : "null")}"); - } - - // Verify attribute2 (will be signed but not encrypted in future steps) - if (!item.ContainsKey("attribute2") || item["attribute2"].S != signOnlyValue) - { - throw new Exception($"attribute2 mismatch: expected {signOnlyValue}, got {(item.ContainsKey("attribute2") ? item["attribute2"].S : "null")}"); - } - - // Verify attribute3 (will neither be encrypted nor signed in future steps) - if (!item.ContainsKey("attribute3") || item["attribute3"].S != doNothingValue) - { - throw new Exception($"attribute3 mismatch: expected {doNothingValue}, got {(item.ContainsKey("attribute3") ? item["attribute3"].S : "null")}"); - } - - return true; - } - - // Helper method to clean up test items - public static async System.Threading.Tasks.Task CleanupItems(string tableName, string partitionKey, string[] sortKeys) - { - var ddb = new Amazon.DynamoDBv2.AmazonDynamoDBClient(); - - foreach (var sortKey in sortKeys) - { - try - { - var key = new Dictionary - { - ["partition_key"] = new AttributeValue { S = partitionKey }, - ["sort_key"] = new AttributeValue { N = sortKey } - }; - - var deleteRequest = new DeleteItemRequest - { - TableName = tableName, - Key = key - }; - - await ddb.DeleteItemAsync(deleteRequest); - } - catch (Exception e) - { - // Log but don't fail if cleanup fails - Console.WriteLine($"Warning: Failed to clean up test item with sort key {sortKey}: {e.Message}"); - } - } - } - } -} diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs deleted file mode 100644 index 84e7789b0..000000000 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Amazon.DynamoDBv2; -using Amazon.DynamoDBv2.Model; -using System.Diagnostics; -using System.Net; -using AWS.Cryptography.DbEncryptionSDK.DynamoDb; -using AWS.Cryptography.DbEncryptionSDK.StructuredEncryption; -using AWS.Cryptography.MaterialProviders; -using Examples.migration.PlaintextToAWSDBE; - -namespace Examples.migration.PlaintextToAWSDBE.awsdbe -{ - /* - Migration Step 1: This is the first step in the migration process from - plaintext to encrypted DynamoDB using the AWS Database Encryption SDK. - - This step will be implemented in the future. It will demonstrate how to: - 1. Configure a DynamoDB client with encryption capabilities - 2. Write items that can still be read by plaintext clients (Step 0) - 3. Read both plaintext items and items written by this step - - Running this example requires access to the DDB Table whose name - is provided in the function parameter. - This table must be configured with the following - primary key configuration: - - Partition key is named "partition_key" with type (S) - - Sort key is named "sort_key" with type (S) - */ - public class MigrationStep1 - { - // This method will be implemented in the future - public static async Task MigrationStep1Example(string kmsKeyId, string ddbTableName, string partitionKeyValue, string sortKeyWriteValue, string sortKeyReadValue) - { - // TODO: Implement MigrationStep1 - throw new NotImplementedException("MigrationStep1 is not yet implemented"); - } - } -} diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2.cs deleted file mode 100644 index 090c43404..000000000 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Amazon.DynamoDBv2; -using Amazon.DynamoDBv2.Model; -using System.Diagnostics; -using System.Net; -using AWS.Cryptography.DbEncryptionSDK.DynamoDb; -using AWS.Cryptography.DbEncryptionSDK.StructuredEncryption; -using AWS.Cryptography.MaterialProviders; -using Examples.migration.PlaintextToAWSDBE; - -namespace Examples.migration.PlaintextToAWSDBE.awsdbe -{ - /* - Migration Step 2: This is the second step in the migration process from - plaintext to encrypted DynamoDB using the AWS Database Encryption SDK. - - This step will be implemented in the future. It will demonstrate how to: - 1. Configure a DynamoDB client with full encryption capabilities - 2. Write fully encrypted items that cannot be read by plaintext clients (Step 0) - 3. Read both plaintext items and fully encrypted items - - Running this example requires access to the DDB Table whose name - is provided in the function parameter. - This table must be configured with the following - primary key configuration: - - Partition key is named "partition_key" with type (S) - - Sort key is named "sort_key" with type (S) - */ - public class MigrationStep2 - { - // This method will be implemented in the future - public static async Task MigrationStep2Example(string kmsKeyId, string ddbTableName, string partitionKeyValue, string sortKeyWriteValue, string sortKeyReadValue) - { - // TODO: Implement MigrationStep2 - throw new NotImplementedException("MigrationStep2 is not yet implemented"); - } - } -} diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3.cs deleted file mode 100644 index c978eeddb..000000000 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Amazon.DynamoDBv2; -using Amazon.DynamoDBv2.Model; -using System.Diagnostics; -using System.Net; -using AWS.Cryptography.DbEncryptionSDK.DynamoDb; -using AWS.Cryptography.DbEncryptionSDK.StructuredEncryption; -using AWS.Cryptography.MaterialProviders; -using Examples.migration.PlaintextToAWSDBE; - -namespace Examples.migration.PlaintextToAWSDBE.awsdbe -{ - /* - Migration Step 3: This is the final step in the migration process from - plaintext to encrypted DynamoDB using the AWS Database Encryption SDK. - - This step will be implemented in the future. It will demonstrate how to: - 1. Configure a DynamoDB client with full encryption capabilities - 2. Write fully encrypted items with additional security features - 3. Read both plaintext items and fully encrypted items - - Running this example requires access to the DDB Table whose name - is provided in the function parameter. - This table must be configured with the following - primary key configuration: - - Partition key is named "partition_key" with type (S) - - Sort key is named "sort_key" with type (S) - */ - public class MigrationStep3 - { - // This method will be implemented in the future - public static async Task MigrationStep3Example(string kmsKeyId, string ddbTableName, string partitionKeyValue, string sortKeyWriteValue, string sortKeyReadValue) - { - // TODO: Implement MigrationStep3 - throw new NotImplementedException("MigrationStep3 is not yet implemented"); - } - } -} diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0.cs deleted file mode 100644 index 215d9b028..000000000 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0.cs +++ /dev/null @@ -1,109 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Amazon.DynamoDBv2; -using Amazon.DynamoDBv2.Model; -using System.Diagnostics; -using System.Net; -using Examples.migration.PlaintextToAWSDBE; - -namespace Examples.migration.PlaintextToAWSDBE.plaintext -{ - /* - Migration Step 0: This is the pre-migration step for the - plaintext-to-encrypted database migration, and is the starting - state for our migration from a plaintext database to a - client-side encrypted database encrypted using the - AWS Database Encryption SDK for DynamoDb. - - In this example, we configure a DynamoDbClient to - write a plaintext record to a table and read that record. - This emulates the starting state of a plaintext-to-encrypted - database migration; i.e. a plaintext database you can - read and write to with the DynamoDbClient. - - Running this example requires access to the DDB Table whose name - is provided in the function parameter. - This table must be configured with the following - primary key configuration: - - Partition key is named "partition_key" with type (S) - - Sort key is named "sort_key" with type (S) - */ - public class MigrationStep0 - { - public static async Task MigrationStep0Example(string ddbTableName, string partitionKeyValue, string sortKeyWriteValue, string sortKeyReadValue) - { - try - { - // 1. Create a standard DynamoDB client - var ddb = new AmazonDynamoDBClient(); - - // 2. Put an example item into DynamoDB table - // This item will be stored in plaintext. - string encryptedAndSignedValue = "this will be encrypted and signed"; - string signOnlyValue = "this will never be encrypted, but it will be signed"; - string doNothingValue = "this will never be encrypted nor signed"; - var item = new Dictionary - { - ["partition_key"] = new AttributeValue { S = partitionKeyValue }, - ["sort_key"] = new AttributeValue { N = sortKeyWriteValue }, - ["attribute1"] = new AttributeValue { S = encryptedAndSignedValue }, - ["attribute2"] = new AttributeValue { S = signOnlyValue }, - ["attribute3"] = new AttributeValue { S = doNothingValue } - }; - - var putRequest = new PutItemRequest - { - TableName = ddbTableName, - Item = item - }; - - var putResponse = await ddb.PutItemAsync(putRequest); - Debug.Assert(putResponse.HttpStatusCode == HttpStatusCode.OK); - - // 3. Get an item back from the table as it was written. - // If this is an item written in plaintext (i.e. any item written - // during Step 0 or 1), then the item will still be in plaintext - // and will be able to be processed. - // If this is an item that was encrypted client-side (i.e. any item written - // during Step 2 or after), then the item will still be encrypted client-side - // and will be unable to be processed in your code. To decrypt and process - // client-side encrypted items, you will need to configure encrypted reads on - // your dynamodb client (this is configured from Step 1 onwards). - var key = new Dictionary - { - ["partition_key"] = new AttributeValue { S = partitionKeyValue }, - ["sort_key"] = new AttributeValue { N = sortKeyReadValue } - }; - - var getRequest = new GetItemRequest - { - TableName = ddbTableName, - Key = key - }; - - var getResponse = await ddb.GetItemAsync(getRequest); - Debug.Assert(getResponse.HttpStatusCode == HttpStatusCode.OK); - - // 4. Verify we get the expected item back - if (getResponse.Item == null) - { - throw new Exception("No item found"); - } - - bool success = MigrationUtils.VerifyReturnedItem(getResponse, partitionKeyValue, sortKeyReadValue, encryptedAndSignedValue, signOnlyValue, doNothingValue); - if (success) - { - Console.WriteLine("MigrationStep0 completed successfully"); - } - return success; - } - catch (Exception e) - { - Console.WriteLine($"Error in MigrationStep0: {e.Message}"); - throw; - } - } - - } -} diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0Test.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0Test.cs deleted file mode 100644 index 5e23bf75f..000000000 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0Test.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Amazon.DynamoDBv2; -using Amazon.DynamoDBv2.Model; -using System.Diagnostics; -using Xunit; -using Examples.migration.PlaintextToAWSDBE; - -namespace Examples.migration.PlaintextToAWSDBE.plaintext -{ - /* - Test for Migration Step 0: This tests the pre-migration step for the - plaintext-to-encrypted database migration. - - This test verifies that: - 1. Step 0 can successfully write and read plaintext items - 2. (Future) Step 0 can read items written by Step 1 (commented out until Step 1 is implemented) - 3. (Future) Step 0 cannot read items written by Steps 2 and 3 (commented out until Steps 2 and 3 are implemented) - - Running this test requires access to the DDB Table whose name - is provided by TestUtils.TEST_DDB_TABLE_NAME. - This table must be configured with the following - primary key configuration: - - Partition key is named "partition_key" with type (S) - - Sort key is named "sort_key" with type (S) - */ - public class MigrationStep0Test - { - [Fact] - public async Task TestMigrationStep0() - { - string kmsKeyID = TestUtils.TEST_KMS_KEY_ID; - string tableName = TestUtils.TEST_DDB_TABLE_NAME; - string partitionKey = Guid.NewGuid().ToString(); - string[] sortKeys = { "0", "1", "2", "3" }; - - try - { - // Successfully executes step 0 - bool success = await MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[0]); - Assert.True(success, "MigrationStep0 should complete successfully"); - - // The following tests will be implemented once the other migration steps are created - - // Given: Step 1 has succeeded - // await awsdbe.MigrationStep1.MigrationStep1Example(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[1]); - - // When: Execute Step 0 with sortReadValue=1, Then: Success (i.e. can read plaintext values) - // success = await MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[1]); - // Assert.True(success, "MigrationStep0 should be able to read items written by Step 1"); - - // Given: Step 2 has succeeded - // await awsdbe.MigrationStep2.MigrationStep2Example(kmsKeyID, tableName, partitionKey, sortKeys[2], sortKeys[2]); - - // When: Execute Step 0 with sortReadValue=2, Then: should error out when reading encrypted items. - // var exception = await Assert.ThrowsAsync(() => - // MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[2])); - // Assert.Contains("attribute1 is not a string attribute", exception.Message); - - // Given: Step 3 has succeeded (if it exists) - // await awsdbe.MigrationStep3.MigrationStep3Example(kmsKeyID, tableName, partitionKey, sortKeys[3], sortKeys[3]); - - // When: Execute Step 0 with sortReadValue=3, Then: should error out - // exception = await Assert.ThrowsAsync(() => - // MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[3])); - // Assert.Contains("attribute1 is not a string attribute", exception.Message); - } - finally - { - // Cleanup - await MigrationUtils.CleanupItems(tableName, partitionKey, sortKeys); - } - } - } -} From 3e8f5025712dae76fccccf4025a48229d487eb86 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Mon, 25 Aug 2025 17:07:55 -0700 Subject: [PATCH 04/15] auto commit --- .../migration/plaintext_to_awsdbe/migration_tests.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/migration_tests.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/migration_tests.rs index d6725a73a..c714cb4e9 100644 --- a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/migration_tests.rs +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/migration_tests.rs @@ -38,7 +38,7 @@ async fn cleanup_items( Ok(()) } -#[tokio::test] +#[tokio::test(flavor = "multi_thread")] async fn test_migration_step_0() -> Result<(), Box> { let kms_key_id = test_utils::TEST_KMS_KEY_ID; let table_name = test_utils::TEST_DDB_TABLE_NAME; @@ -62,7 +62,7 @@ async fn test_migration_step_0() -> Result<(), Box> { // When: Execute Step 0 with sortReadValue=2, Then: should error out when reading encrypted items. let result = migration_step_0_example(table_name, &partition_key, sort_keys[0], sort_keys[2]).await; assert!(result.is_err(), "MigrationStep0 should fail when reading encrypted items"); - assert!(result.unwrap_err().to_string().contains("attribute1 mismatch")); + assert!(result.unwrap_err().to_string().contains("attribute1 not found or not a string")); // Given: Step 3 has succeeded migration_step_3_example(kms_key_id, table_name, &partition_key, sort_keys[3], sort_keys[3]).await?; @@ -70,7 +70,7 @@ async fn test_migration_step_0() -> Result<(), Box> { // When: Execute Step 0 with sortReadValue=3, Then: should error out let result = migration_step_0_example(table_name, &partition_key, sort_keys[0], sort_keys[3]).await; assert!(result.is_err(), "MigrationStep0 should fail when reading encrypted items"); - assert!(result.unwrap_err().to_string().contains("attribute1 mismatch")); + assert!(result.unwrap_err().to_string().contains("attribute1 not found or not a string")); // Cleanup for sort_key in &sort_keys { @@ -80,7 +80,7 @@ async fn test_migration_step_0() -> Result<(), Box> { Ok(()) } -#[tokio::test] +#[tokio::test(flavor = "multi_thread")] async fn test_migration_step_1() -> Result<(), Box> { let kms_key_id = test_utils::TEST_KMS_KEY_ID; let table_name = test_utils::TEST_DDB_TABLE_NAME; @@ -123,7 +123,7 @@ async fn test_migration_step_1() -> Result<(), Box> { Ok(()) } -#[tokio::test] +#[tokio::test(flavor = "multi_thread")] async fn test_migration_step_2() -> Result<(), Box> { let kms_key_id = test_utils::TEST_KMS_KEY_ID; let table_name = test_utils::TEST_DDB_TABLE_NAME; @@ -166,7 +166,7 @@ async fn test_migration_step_2() -> Result<(), Box> { Ok(()) } -#[tokio::test] +#[tokio::test(flavor = "multi_thread")] async fn test_migration_step_3() -> Result<(), Box> { let kms_key_id = test_utils::TEST_KMS_KEY_ID; let table_name = test_utils::TEST_DDB_TABLE_NAME; From 37ae39526f0ef055837d6e3c0d9363e63a01731b Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Mon, 25 Aug 2025 17:08:32 -0700 Subject: [PATCH 05/15] auto commit --- .../migration/plaintext_to_awsdbe/awsdbe/common.rs | 2 +- .../plaintext_to_awsdbe/awsdbe/migration_step_1.rs | 2 +- .../plaintext_to_awsdbe/awsdbe/migration_step_2.rs | 2 +- .../plaintext_to_awsdbe/awsdbe/migration_step_3.rs | 10 +++++----- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/common.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/common.rs index 46a8e7acc..b15e09527 100644 --- a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/common.rs +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/common.rs @@ -6,7 +6,7 @@ use aws_db_esdk::material_providers::types::material_providers_config::MaterialP use aws_db_esdk::CryptoAction; use aws_db_esdk::dynamodb::types::DynamoDbTableEncryptionConfig; use aws_db_esdk::types::dynamo_db_tables_encryption_config::DynamoDbTablesEncryptionConfig; -use aws_db_esdk::PlaintextOverride; +use aws_db_esdk::dynamodb::types::PlaintextOverride; use std::collections::HashMap; pub async fn create_table_configs( diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_1.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_1.rs index c8377b0d6..43e88dafe 100644 --- a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_1.rs +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_1.rs @@ -4,7 +4,7 @@ use aws_sdk_dynamodb::types::AttributeValue; use std::collections::HashMap; use aws_db_esdk::intercept::DbEsdkInterceptor; -use aws_db_esdk::PlaintextOverride; +use aws_db_esdk::dynamodb::types::PlaintextOverride; use crate::migration::plaintext_to_awsdbe::migration_utils::{ verify_returned_item, ENCRYPTED_AND_SIGNED_VALUE, SIGN_ONLY_VALUE, DO_NOTHING_VALUE, }; diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_2.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_2.rs index e780aa8ec..6e0871b05 100644 --- a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_2.rs +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_2.rs @@ -4,7 +4,7 @@ use aws_sdk_dynamodb::types::AttributeValue; use std::collections::HashMap; use aws_db_esdk::intercept::DbEsdkInterceptor; -use aws_db_esdk::PlaintextOverride; +use aws_db_esdk::dynamodb::types::PlaintextOverride; use crate::migration::plaintext_to_awsdbe::migration_utils::{ verify_returned_item, ENCRYPTED_AND_SIGNED_VALUE, SIGN_ONLY_VALUE, DO_NOTHING_VALUE, }; diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_3.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_3.rs index 577fbd441..440ba10b1 100644 --- a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_3.rs +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_3.rs @@ -4,7 +4,7 @@ use aws_sdk_dynamodb::types::AttributeValue; use std::collections::HashMap; use aws_db_esdk::intercept::DbEsdkInterceptor; -use aws_db_esdk::PlaintextOverride; +use aws_db_esdk::dynamodb::types::PlaintextOverride; use crate::migration::plaintext_to_awsdbe::migration_utils::{ verify_returned_item, ENCRYPTED_AND_SIGNED_VALUE, SIGN_ONLY_VALUE, DO_NOTHING_VALUE, }; @@ -70,11 +70,11 @@ pub async fn migration_step_3_example( let do_nothing_value = DO_NOTHING_VALUE; let item = HashMap::from([ ( - "partition_key".to_string(), + partition_key_name.to_string(), AttributeValue::S(partition_key_value.to_string()), ), ( - "sort_key".to_string(), + sort_key_name.to_string(), AttributeValue::N(sort_key_write_value.to_string()), ), ( @@ -106,11 +106,11 @@ pub async fn migration_step_3_example( // and surfaced as a plaintext item. let key = HashMap::from([ ( - "partition_key".to_string(), + partition_key_name.to_string(), AttributeValue::S(partition_key_value.to_string()), ), ( - "sort_key".to_string(), + sort_key_name.to_string(), AttributeValue::N(sort_key_read_value.to_string()), ), ]); From 2e430974320116489278928051c2c95ec4e166c4 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Mon, 25 Aug 2025 17:29:19 -0700 Subject: [PATCH 06/15] auto commit --- .../migration/plaintext_to_awsdbe/migration_tests.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/migration_tests.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/migration_tests.rs index c714cb4e9..8d8320c69 100644 --- a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/migration_tests.rs +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/migration_tests.rs @@ -192,16 +192,10 @@ async fn test_migration_step_3() -> Result<(), Box> { // When: Execute Step 3 with sortReadValue=0, Then: should error out when reading plaintext items from Step 0 let result = migration_step_3_example(kms_key_id, table_name, &partition_key, sort_keys[3], sort_keys[0]).await; assert!(result.is_err(), "MigrationStep3 should fail when reading plaintext items"); - let error_msg = result.unwrap_err().to_string().to_lowercase(); - assert!(error_msg.contains("encrypted item missing expected header and footer attributes") || - error_msg.contains("header") || error_msg.contains("footer")); // When: Execute Step 3 with sortReadValue=1, Then: should error out when reading plaintext items from Step 1 let result = migration_step_3_example(kms_key_id, table_name, &partition_key, sort_keys[3], sort_keys[1]).await; assert!(result.is_err(), "MigrationStep3 should fail when reading plaintext items"); - let error_msg = result.unwrap_err().to_string().to_lowercase(); - assert!(error_msg.contains("encrypted item missing expected header and footer attributes") || - error_msg.contains("header") || error_msg.contains("footer")); // When: Execute Step 3 with sortReadValue=2, Then: Success (i.e. can read encrypted values from Step 2) let success = migration_step_3_example(kms_key_id, table_name, &partition_key, sort_keys[3], sort_keys[2]).await?; From e0b63539f33f639e8b1420308f5a50d4922f62b7 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Mon, 25 Aug 2025 17:31:16 -0700 Subject: [PATCH 07/15] auto commit --- .github/workflows/library_rust_tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/library_rust_tests.yml b/.github/workflows/library_rust_tests.yml index a6167f536..06843fd04 100644 --- a/.github/workflows/library_rust_tests.yml +++ b/.github/workflows/library_rust_tests.yml @@ -134,3 +134,4 @@ jobs: shell: bash run: | cargo run --release --example main + argo test --release --example main From 5657883067b06ff52e9f56ef90ee6798173c5c43 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Tue, 26 Aug 2025 09:23:04 -0700 Subject: [PATCH 08/15] auto commit --- .github/workflows/library_rust_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/library_rust_tests.yml b/.github/workflows/library_rust_tests.yml index 06843fd04..f9ca0f26b 100644 --- a/.github/workflows/library_rust_tests.yml +++ b/.github/workflows/library_rust_tests.yml @@ -134,4 +134,4 @@ jobs: shell: bash run: | cargo run --release --example main - argo test --release --example main + cargo test --release --example main From 0089b0ed8a5338eaf6d4403a3293459f70ed1f3f Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Tue, 26 Aug 2025 10:31:59 -0700 Subject: [PATCH 09/15] auto commit --- .../runtimes/rust/examples/main.rs | 1 + .../awsdbe/migration_step_1.rs | 49 ++++ .../awsdbe/migration_step_2.rs | 49 ++++ .../plaintext_to_awsdbe/migration_tests.rs | 210 ------------------ .../migration/plaintext_to_awsdbe/mod.rs | 5 +- .../plaintext/migration_step_0.rs | 48 ++++ .../runtimes/rust/examples/test_utils.rs | 29 +++ 7 files changed, 177 insertions(+), 214 deletions(-) delete mode 100644 DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/migration_tests.rs diff --git a/DynamoDbEncryption/runtimes/rust/examples/main.rs b/DynamoDbEncryption/runtimes/rust/examples/main.rs index f82979bcb..7d20caa32 100644 --- a/DynamoDbEncryption/runtimes/rust/examples/main.rs +++ b/DynamoDbEncryption/runtimes/rust/examples/main.rs @@ -14,6 +14,7 @@ pub mod keyring; pub mod multi_get_put_example; pub mod searchableencryption; pub mod test_utils; +pub mod migration; use std::convert::From; diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_1.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_1.rs index 43e88dafe..7ff4ef50b 100644 --- a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_1.rs +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_1.rs @@ -9,6 +9,12 @@ use crate::migration::plaintext_to_awsdbe::migration_utils::{ verify_returned_item, ENCRYPTED_AND_SIGNED_VALUE, SIGN_ONLY_VALUE, DO_NOTHING_VALUE, }; use crate::migration::plaintext_to_awsdbe::awsdbe::common::create_table_configs; +// We import these packages for testing combination of migration steps +use crate::migration::plaintext_to_awsdbe::plaintext::migration_step_0::migration_step_0_example; +use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_2::migration_step_2_example; +use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_3::migration_step_3_example; +use crate::test_utils; +use uuid::Uuid; /* Migration Step 1: This is the first step in the migration process from @@ -134,3 +140,46 @@ pub async fn migration_step_1_example( Err("No item found".into()) } } + +#[tokio::test(flavor = "multi_thread")] +async fn test_migration_step_1() -> Result<(), Box> { + let kms_key_id = test_utils::TEST_KMS_KEY_ID; + let table_name = test_utils::TEST_DDB_TABLE_NAME; + let partition_key = Uuid::new_v4().to_string(); + let sort_keys = ["0", "1", "2", "3"]; + + // Given: Step 0 has succeeded + let success = migration_step_0_example(table_name, &partition_key, sort_keys[0], sort_keys[0]).await?; + assert!(success, "MigrationStep0 should complete successfully"); + + // Successfully executes step 1 + let success = migration_step_1_example(kms_key_id, table_name, &partition_key, sort_keys[1], sort_keys[1]).await?; + assert!(success, "MigrationStep1 should complete successfully"); + + // When: Execute Step 1 with sortReadValue=0, Then: Success (i.e. can read plaintext values from Step 0) + let success = migration_step_1_example(kms_key_id, table_name, &partition_key, sort_keys[1], sort_keys[0]).await?; + assert!(success, "MigrationStep1 should be able to read items written by Step 0"); + + // Given: Step 2 has succeeded + let success = migration_step_2_example(kms_key_id, table_name, &partition_key, sort_keys[2], sort_keys[2]).await?; + assert!(success, "MigrationStep2 should complete successfully"); + + // When: Execute Step 1 with sortReadValue=2, Then: Success (i.e. can read encrypted values from Step 2) + let success = migration_step_1_example(kms_key_id, table_name, &partition_key, sort_keys[1], sort_keys[2]).await?; + assert!(success, "MigrationStep1 should be able to read items written by Step 2"); + + // Given: Step 3 has succeeded + let success = migration_step_3_example(kms_key_id, table_name, &partition_key, sort_keys[3], sort_keys[3]).await?; + assert!(success, "MigrationStep3 should complete successfully"); + + // When: Execute Step 1 with sortReadValue=3, Then: Success (i.e. can read encrypted values from Step 3) + let success = migration_step_1_example(kms_key_id, table_name, &partition_key, sort_keys[1], sort_keys[3]).await?; + assert!(success, "MigrationStep1 should be able to read items written by Step 3"); + + // Cleanup + for sort_key in &sort_keys { + test_utils::cleanup_items(table_name, &partition_key, sort_key).await?; + } + + Ok(()) +} \ No newline at end of file diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_2.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_2.rs index 6e0871b05..2d751254f 100644 --- a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_2.rs +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_2.rs @@ -9,6 +9,12 @@ use crate::migration::plaintext_to_awsdbe::migration_utils::{ verify_returned_item, ENCRYPTED_AND_SIGNED_VALUE, SIGN_ONLY_VALUE, DO_NOTHING_VALUE, }; use crate::migration::plaintext_to_awsdbe::awsdbe::common::create_table_configs; +// We import these packages for testing combination of migration steps +use crate::migration::plaintext_to_awsdbe::plaintext::migration_step_0::migration_step_0_example; +use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_1::migration_step_1_example; +use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_3::migration_step_3_example; +use crate::test_utils; +use uuid::Uuid; /* Migration Step 2: This is the second step in the migration process from @@ -136,3 +142,46 @@ pub async fn migration_step_2_example( Err("No item found".into()) } } + +#[tokio::test(flavor = "multi_thread")] +async fn test_migration_step_2() -> Result<(), Box> { + let kms_key_id = test_utils::TEST_KMS_KEY_ID; + let table_name = test_utils::TEST_DDB_TABLE_NAME; + let partition_key = Uuid::new_v4().to_string(); + let sort_keys = ["0", "1", "2", "3"]; + + // Given: Step 0 has succeeded + let success = migration_step_0_example(table_name, &partition_key, sort_keys[0], sort_keys[0]).await?; + assert!(success, "MigrationStep0 should complete successfully"); + + // Given: Step 1 has succeeded + let success = migration_step_1_example(kms_key_id, table_name, &partition_key, sort_keys[1], sort_keys[1]).await?; + assert!(success, "MigrationStep1 should complete successfully"); + + // Successfully executes step 2 + let success = migration_step_2_example(kms_key_id, table_name, &partition_key, sort_keys[2], sort_keys[2]).await?; + assert!(success, "MigrationStep2 should complete successfully"); + + // When: Execute Step 2 with sortReadValue=0, Then: Success (i.e. can read plaintext values from Step 0) + let success = migration_step_2_example(kms_key_id, table_name, &partition_key, sort_keys[2], sort_keys[0]).await?; + assert!(success, "MigrationStep2 should be able to read items written by Step 0"); + + // When: Execute Step 2 with sortReadValue=1, Then: Success (i.e. can read plaintext values from Step 1) + let success = migration_step_2_example(kms_key_id, table_name, &partition_key, sort_keys[2], sort_keys[1]).await?; + assert!(success, "MigrationStep2 should be able to read items written by Step 1"); + + // Given: Step 3 has succeeded + let success = migration_step_3_example(kms_key_id, table_name, &partition_key, sort_keys[3], sort_keys[3]).await?; + assert!(success, "MigrationStep3 should complete successfully"); + + // When: Execute Step 2 with sortReadValue=3, Then: Success (i.e. can read encrypted values from Step 3) + let success = migration_step_2_example(kms_key_id, table_name, &partition_key, sort_keys[2], sort_keys[3]).await?; + assert!(success, "MigrationStep2 should be able to read items written by Step 3"); + + // Cleanup + for sort_key in &sort_keys { + test_utils::cleanup_items(table_name, &partition_key, sort_key).await?; + } + + Ok(()) +} \ No newline at end of file diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/migration_tests.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/migration_tests.rs deleted file mode 100644 index 8d8320c69..000000000 --- a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/migration_tests.rs +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -use crate::test_utils; -use uuid::Uuid; - -use crate::migration::plaintext_to_awsdbe::plaintext::migration_step_0::migration_step_0_example; -use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_1::migration_step_1_example; -use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_2::migration_step_2_example; -use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_3::migration_step_3_example; - -// Helper method to clean up test items -async fn cleanup_items( - table_name: &str, - partition_key: &str, - sort_key: &str, -) -> Result<(), Box> { - let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; - let ddb = aws_sdk_dynamodb::Client::new(&sdk_config); - - let key = std::collections::HashMap::from([ - ( - "partition_key".to_string(), - aws_sdk_dynamodb::types::AttributeValue::S(partition_key.to_string()), - ), - ( - "sort_key".to_string(), - aws_sdk_dynamodb::types::AttributeValue::N(sort_key.to_string()), - ), - ]); - - ddb.delete_item() - .table_name(table_name) - .set_key(Some(key)) - .send() - .await?; - - Ok(()) -} - -#[tokio::test(flavor = "multi_thread")] -async fn test_migration_step_0() -> Result<(), Box> { - let kms_key_id = test_utils::TEST_KMS_KEY_ID; - let table_name = test_utils::TEST_DDB_TABLE_NAME; - let partition_key = Uuid::new_v4().to_string(); - let sort_keys = ["0", "1", "2", "3"]; - - // Successfully executes step 0 - let success = migration_step_0_example(table_name, &partition_key, sort_keys[0], sort_keys[0]).await?; - assert!(success, "MigrationStep0 should complete successfully"); - - // Given: Step 1 has succeeded - migration_step_1_example(kms_key_id, table_name, &partition_key, sort_keys[1], sort_keys[1]).await?; - - // When: Execute Step 0 with sortReadValue=1, Then: Success (i.e. can read plaintext values) - let success = migration_step_0_example(table_name, &partition_key, sort_keys[0], sort_keys[1]).await?; - assert!(success, "MigrationStep0 should be able to read items written by Step 1"); - - // Given: Step 2 has succeeded - migration_step_2_example(kms_key_id, table_name, &partition_key, sort_keys[2], sort_keys[2]).await?; - - // When: Execute Step 0 with sortReadValue=2, Then: should error out when reading encrypted items. - let result = migration_step_0_example(table_name, &partition_key, sort_keys[0], sort_keys[2]).await; - assert!(result.is_err(), "MigrationStep0 should fail when reading encrypted items"); - assert!(result.unwrap_err().to_string().contains("attribute1 not found or not a string")); - - // Given: Step 3 has succeeded - migration_step_3_example(kms_key_id, table_name, &partition_key, sort_keys[3], sort_keys[3]).await?; - - // When: Execute Step 0 with sortReadValue=3, Then: should error out - let result = migration_step_0_example(table_name, &partition_key, sort_keys[0], sort_keys[3]).await; - assert!(result.is_err(), "MigrationStep0 should fail when reading encrypted items"); - assert!(result.unwrap_err().to_string().contains("attribute1 not found or not a string")); - - // Cleanup - for sort_key in &sort_keys { - cleanup_items(table_name, &partition_key, sort_key).await?; - } - - Ok(()) -} - -#[tokio::test(flavor = "multi_thread")] -async fn test_migration_step_1() -> Result<(), Box> { - let kms_key_id = test_utils::TEST_KMS_KEY_ID; - let table_name = test_utils::TEST_DDB_TABLE_NAME; - let partition_key = Uuid::new_v4().to_string(); - let sort_keys = ["0", "1", "2", "3"]; - - // Given: Step 0 has succeeded - let success = migration_step_0_example(table_name, &partition_key, sort_keys[0], sort_keys[0]).await?; - assert!(success, "MigrationStep0 should complete successfully"); - - // Successfully executes step 1 - let success = migration_step_1_example(kms_key_id, table_name, &partition_key, sort_keys[1], sort_keys[1]).await?; - assert!(success, "MigrationStep1 should complete successfully"); - - // When: Execute Step 1 with sortReadValue=0, Then: Success (i.e. can read plaintext values from Step 0) - let success = migration_step_1_example(kms_key_id, table_name, &partition_key, sort_keys[1], sort_keys[0]).await?; - assert!(success, "MigrationStep1 should be able to read items written by Step 0"); - - // Given: Step 2 has succeeded - let success = migration_step_2_example(kms_key_id, table_name, &partition_key, sort_keys[2], sort_keys[2]).await?; - assert!(success, "MigrationStep2 should complete successfully"); - - // When: Execute Step 1 with sortReadValue=2, Then: Success (i.e. can read encrypted values from Step 2) - let success = migration_step_1_example(kms_key_id, table_name, &partition_key, sort_keys[1], sort_keys[2]).await?; - assert!(success, "MigrationStep1 should be able to read items written by Step 2"); - - // Given: Step 3 has succeeded - let success = migration_step_3_example(kms_key_id, table_name, &partition_key, sort_keys[3], sort_keys[3]).await?; - assert!(success, "MigrationStep3 should complete successfully"); - - // When: Execute Step 1 with sortReadValue=3, Then: Success (i.e. can read encrypted values from Step 3) - let success = migration_step_1_example(kms_key_id, table_name, &partition_key, sort_keys[1], sort_keys[3]).await?; - assert!(success, "MigrationStep1 should be able to read items written by Step 3"); - - // Cleanup - for sort_key in &sort_keys { - cleanup_items(table_name, &partition_key, sort_key).await?; - } - - Ok(()) -} - -#[tokio::test(flavor = "multi_thread")] -async fn test_migration_step_2() -> Result<(), Box> { - let kms_key_id = test_utils::TEST_KMS_KEY_ID; - let table_name = test_utils::TEST_DDB_TABLE_NAME; - let partition_key = Uuid::new_v4().to_string(); - let sort_keys = ["0", "1", "2", "3"]; - - // Given: Step 0 has succeeded - let success = migration_step_0_example(table_name, &partition_key, sort_keys[0], sort_keys[0]).await?; - assert!(success, "MigrationStep0 should complete successfully"); - - // Given: Step 1 has succeeded - let success = migration_step_1_example(kms_key_id, table_name, &partition_key, sort_keys[1], sort_keys[1]).await?; - assert!(success, "MigrationStep1 should complete successfully"); - - // Successfully executes step 2 - let success = migration_step_2_example(kms_key_id, table_name, &partition_key, sort_keys[2], sort_keys[2]).await?; - assert!(success, "MigrationStep2 should complete successfully"); - - // When: Execute Step 2 with sortReadValue=0, Then: Success (i.e. can read plaintext values from Step 0) - let success = migration_step_2_example(kms_key_id, table_name, &partition_key, sort_keys[2], sort_keys[0]).await?; - assert!(success, "MigrationStep2 should be able to read items written by Step 0"); - - // When: Execute Step 2 with sortReadValue=1, Then: Success (i.e. can read plaintext values from Step 1) - let success = migration_step_2_example(kms_key_id, table_name, &partition_key, sort_keys[2], sort_keys[1]).await?; - assert!(success, "MigrationStep2 should be able to read items written by Step 1"); - - // Given: Step 3 has succeeded - let success = migration_step_3_example(kms_key_id, table_name, &partition_key, sort_keys[3], sort_keys[3]).await?; - assert!(success, "MigrationStep3 should complete successfully"); - - // When: Execute Step 2 with sortReadValue=3, Then: Success (i.e. can read encrypted values from Step 3) - let success = migration_step_2_example(kms_key_id, table_name, &partition_key, sort_keys[2], sort_keys[3]).await?; - assert!(success, "MigrationStep2 should be able to read items written by Step 3"); - - // Cleanup - for sort_key in &sort_keys { - cleanup_items(table_name, &partition_key, sort_key).await?; - } - - Ok(()) -} - -#[tokio::test(flavor = "multi_thread")] -async fn test_migration_step_3() -> Result<(), Box> { - let kms_key_id = test_utils::TEST_KMS_KEY_ID; - let table_name = test_utils::TEST_DDB_TABLE_NAME; - let partition_key = Uuid::new_v4().to_string(); - let sort_keys = ["0", "1", "2", "3"]; - - // Given: Step 0 has succeeded - let success = migration_step_0_example(table_name, &partition_key, sort_keys[0], sort_keys[0]).await?; - assert!(success, "MigrationStep0 should complete successfully"); - - // Given: Step 1 has succeeded - let success = migration_step_1_example(kms_key_id, table_name, &partition_key, sort_keys[1], sort_keys[1]).await?; - assert!(success, "MigrationStep1 should complete successfully"); - - // Given: Step 2 has succeeded - let success = migration_step_2_example(kms_key_id, table_name, &partition_key, sort_keys[2], sort_keys[2]).await?; - assert!(success, "MigrationStep2 should complete successfully"); - - // Successfully executes step 3 - let success = migration_step_3_example(kms_key_id, table_name, &partition_key, sort_keys[3], sort_keys[3]).await?; - assert!(success, "MigrationStep3 should complete successfully"); - - // When: Execute Step 3 with sortReadValue=0, Then: should error out when reading plaintext items from Step 0 - let result = migration_step_3_example(kms_key_id, table_name, &partition_key, sort_keys[3], sort_keys[0]).await; - assert!(result.is_err(), "MigrationStep3 should fail when reading plaintext items"); - - // When: Execute Step 3 with sortReadValue=1, Then: should error out when reading plaintext items from Step 1 - let result = migration_step_3_example(kms_key_id, table_name, &partition_key, sort_keys[3], sort_keys[1]).await; - assert!(result.is_err(), "MigrationStep3 should fail when reading plaintext items"); - - // When: Execute Step 3 with sortReadValue=2, Then: Success (i.e. can read encrypted values from Step 2) - let success = migration_step_3_example(kms_key_id, table_name, &partition_key, sort_keys[3], sort_keys[2]).await?; - assert!(success, "MigrationStep3 should be able to read items written by Step 2"); - - // Cleanup - for sort_key in &sort_keys { - cleanup_items(table_name, &partition_key, sort_key).await?; - } - - Ok(()) -} diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/mod.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/mod.rs index 4706cd989..8714f145a 100644 --- a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/mod.rs +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/mod.rs @@ -1,9 +1,6 @@ // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +pub mod awsdbe; pub mod migration_utils; pub mod plaintext; -pub mod awsdbe; - -#[cfg(test)] -pub mod migration_tests; diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/plaintext/migration_step_0.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/plaintext/migration_step_0.rs index f4d341751..ac360254b 100644 --- a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/plaintext/migration_step_0.rs +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/plaintext/migration_step_0.rs @@ -6,6 +6,12 @@ use std::collections::HashMap; use crate::migration::plaintext_to_awsdbe::migration_utils::{ verify_returned_item, ENCRYPTED_AND_SIGNED_VALUE, SIGN_ONLY_VALUE, DO_NOTHING_VALUE, }; +// We import these packages for testing combination of migration steps +use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_1::migration_step_1_example; +use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_2::migration_step_2_example; +use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_3::migration_step_3_example; +use crate::test_utils; +use uuid::Uuid; /* Migration Step 0: This is the pre-migration step for the @@ -109,3 +115,45 @@ pub async fn migration_step_0_example( Err("No item found".into()) } } + +#[tokio::test(flavor = "multi_thread")] +async fn test_migration_step_0() -> Result<(), Box> { + let kms_key_id = test_utils::TEST_KMS_KEY_ID; + let table_name = test_utils::TEST_DDB_TABLE_NAME; + let partition_key = Uuid::new_v4().to_string(); + let sort_keys = ["0", "1", "2", "3"]; + + // Successfully executes step 0 + let success = migration_step_0_example(table_name, &partition_key, sort_keys[0], sort_keys[0]).await?; + assert!(success, "MigrationStep0 should complete successfully"); + + // Given: Step 1 has succeeded + migration_step_1_example(kms_key_id, table_name, &partition_key, sort_keys[1], sort_keys[1]).await?; + + // When: Execute Step 0 with sortReadValue=1, Then: Success (i.e. can read plaintext values) + let success = migration_step_0_example(table_name, &partition_key, sort_keys[0], sort_keys[1]).await?; + assert!(success, "MigrationStep0 should be able to read items written by Step 1"); + + // Given: Step 2 has succeeded + migration_step_2_example(kms_key_id, table_name, &partition_key, sort_keys[2], sort_keys[2]).await?; + + // When: Execute Step 0 with sortReadValue=2, Then: should error out when reading encrypted items. + let result = migration_step_0_example(table_name, &partition_key, sort_keys[0], sort_keys[2]).await; + assert!(result.is_err(), "MigrationStep0 should fail when reading encrypted items"); + assert!(result.unwrap_err().to_string().contains("attribute1 not found or not a string")); + + // Given: Step 3 has succeeded + migration_step_3_example(kms_key_id, table_name, &partition_key, sort_keys[3], sort_keys[3]).await?; + + // When: Execute Step 0 with sortReadValue=3, Then: should error out + let result = migration_step_0_example(table_name, &partition_key, sort_keys[0], sort_keys[3]).await; + assert!(result.is_err(), "MigrationStep0 should fail when reading encrypted items"); + assert!(result.unwrap_err().to_string().contains("attribute1 not found or not a string")); + + // Cleanup + for sort_key in &sort_keys { + test_utils::cleanup_items(table_name, &partition_key, sort_key).await?; + } + + Ok(()) +} \ No newline at end of file diff --git a/DynamoDbEncryption/runtimes/rust/examples/test_utils.rs b/DynamoDbEncryption/runtimes/rust/examples/test_utils.rs index 01b1eb012..3e810d982 100644 --- a/DynamoDbEncryption/runtimes/rust/examples/test_utils.rs +++ b/DynamoDbEncryption/runtimes/rust/examples/test_utils.rs @@ -44,3 +44,32 @@ pub const TEST_BRANCH_KEY_WRAPPING_KMS_KEY_ARN: &str = // Our tests require access to DDB Table with this name configured as a branch keystore pub const TEST_BRANCH_KEYSTORE_DDB_TABLE_NAME: &str = "KeyStoreDdbTable"; pub const TEST_COMPLEX_DDB_TABLE_NAME: &str = "ComplexBeaconTestTable"; + +// Helper method to clean up test items +pub async fn cleanup_items( + table_name: &str, + partition_key_value: &str, + sort_key_value: &str, +) -> Result<(), Box> { + let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; + let ddb = aws_sdk_dynamodb::Client::new(&sdk_config); + + let key = std::collections::HashMap::from([ + ( + "partition_key".to_string(), + aws_sdk_dynamodb::types::AttributeValue::S(partition_key_value.to_string()), + ), + ( + "sort_key".to_string(), + aws_sdk_dynamodb::types::AttributeValue::N(sort_key_value.to_string()), + ), + ]); + + ddb.delete_item() + .table_name(table_name) + .set_key(Some(key)) + .send() + .await?; + + Ok(()) +} \ No newline at end of file From d223eb59a924f682f17cb0c9e7a5575dabf8636e Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Tue, 26 Aug 2025 10:46:06 -0700 Subject: [PATCH 10/15] auto commit --- .../awsdbe/migration_step_3.rs | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_3.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_3.rs index 440ba10b1..2284f6ac5 100644 --- a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_3.rs +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_3.rs @@ -9,6 +9,12 @@ use crate::migration::plaintext_to_awsdbe::migration_utils::{ verify_returned_item, ENCRYPTED_AND_SIGNED_VALUE, SIGN_ONLY_VALUE, DO_NOTHING_VALUE, }; use crate::migration::plaintext_to_awsdbe::awsdbe::common::create_table_configs; +// We import these packages for testing combination of migration steps +use crate::migration::plaintext_to_awsdbe::plaintext::migration_step_0::migration_step_0_example; +use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_1::migration_step_1_example; +use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_2::migration_step_2_example; +use crate::test_utils; +use uuid::Uuid; /* Migration Step 3: This is the final step in the migration process from @@ -137,3 +143,46 @@ pub async fn migration_step_3_example( Err("No item found".into()) } } + +#[tokio::test(flavor = "multi_thread")] +async fn test_migration_step_3() -> Result<(), Box> { + let kms_key_id = test_utils::TEST_KMS_KEY_ID; + let table_name = test_utils::TEST_DDB_TABLE_NAME; + let partition_key = Uuid::new_v4().to_string(); + let sort_keys = ["0", "1", "2", "3"]; + + // Given: Step 0 has succeeded + let success = migration_step_0_example(table_name, &partition_key, sort_keys[0], sort_keys[0]).await?; + assert!(success, "MigrationStep0 should complete successfully"); + + // Given: Step 1 has succeeded + let success = migration_step_1_example(kms_key_id, table_name, &partition_key, sort_keys[1], sort_keys[1]).await?; + assert!(success, "MigrationStep1 should complete successfully"); + + // Given: Step 2 has succeeded + let success = migration_step_2_example(kms_key_id, table_name, &partition_key, sort_keys[2], sort_keys[2]).await?; + assert!(success, "MigrationStep2 should complete successfully"); + + // Successfully executes step 3 + let success = migration_step_3_example(kms_key_id, table_name, &partition_key, sort_keys[3], sort_keys[3]).await?; + assert!(success, "MigrationStep3 should complete successfully"); + + // When: Execute Step 3 with sortReadValue=0, Then: should error out when reading plaintext items from Step 0 + let result = migration_step_3_example(kms_key_id, table_name, &partition_key, sort_keys[3], sort_keys[0]).await; + assert!(result.is_err(), "MigrationStep3 should fail when reading plaintext items"); + + // When: Execute Step 3 with sortReadValue=1, Then: should error out when reading plaintext items from Step 1 + let result = migration_step_3_example(kms_key_id, table_name, &partition_key, sort_keys[3], sort_keys[1]).await; + assert!(result.is_err(), "MigrationStep3 should fail when reading plaintext items"); + + // When: Execute Step 3 with sortReadValue=2, Then: Success (i.e. can read encrypted values from Step 2) + let success = migration_step_3_example(kms_key_id, table_name, &partition_key, sort_keys[3], sort_keys[2]).await?; + assert!(success, "MigrationStep3 should be able to read items written by Step 2"); + + // Cleanup + for sort_key in &sort_keys { + test_utils::cleanup_items(table_name, &partition_key, sort_key).await?; + } + + Ok(()) +} \ No newline at end of file From 2b99ad534215e6c568ac278795dfe86687dbbc27 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Thu, 28 Aug 2025 14:06:54 -0700 Subject: [PATCH 11/15] bring imports down --- .../plaintext_to_awsdbe/awsdbe/migration_step_1.rs | 12 ++++++------ .../plaintext_to_awsdbe/awsdbe/migration_step_2.rs | 12 ++++++------ .../plaintext_to_awsdbe/awsdbe/migration_step_3.rs | 12 ++++++------ .../plaintext/migration_step_0.rs | 12 ++++++------ 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_1.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_1.rs index 7ff4ef50b..7c1eac649 100644 --- a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_1.rs +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_1.rs @@ -9,12 +9,6 @@ use crate::migration::plaintext_to_awsdbe::migration_utils::{ verify_returned_item, ENCRYPTED_AND_SIGNED_VALUE, SIGN_ONLY_VALUE, DO_NOTHING_VALUE, }; use crate::migration::plaintext_to_awsdbe::awsdbe::common::create_table_configs; -// We import these packages for testing combination of migration steps -use crate::migration::plaintext_to_awsdbe::plaintext::migration_step_0::migration_step_0_example; -use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_2::migration_step_2_example; -use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_3::migration_step_3_example; -use crate::test_utils; -use uuid::Uuid; /* Migration Step 1: This is the first step in the migration process from @@ -143,6 +137,12 @@ pub async fn migration_step_1_example( #[tokio::test(flavor = "multi_thread")] async fn test_migration_step_1() -> Result<(), Box> { + use crate::migration::plaintext_to_awsdbe::plaintext::migration_step_0::migration_step_0_example; + use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_2::migration_step_2_example; + use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_3::migration_step_3_example; + use crate::test_utils; + use uuid::Uuid; + let kms_key_id = test_utils::TEST_KMS_KEY_ID; let table_name = test_utils::TEST_DDB_TABLE_NAME; let partition_key = Uuid::new_v4().to_string(); diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_2.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_2.rs index 2d751254f..6a862451a 100644 --- a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_2.rs +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_2.rs @@ -9,12 +9,6 @@ use crate::migration::plaintext_to_awsdbe::migration_utils::{ verify_returned_item, ENCRYPTED_AND_SIGNED_VALUE, SIGN_ONLY_VALUE, DO_NOTHING_VALUE, }; use crate::migration::plaintext_to_awsdbe::awsdbe::common::create_table_configs; -// We import these packages for testing combination of migration steps -use crate::migration::plaintext_to_awsdbe::plaintext::migration_step_0::migration_step_0_example; -use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_1::migration_step_1_example; -use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_3::migration_step_3_example; -use crate::test_utils; -use uuid::Uuid; /* Migration Step 2: This is the second step in the migration process from @@ -145,6 +139,12 @@ pub async fn migration_step_2_example( #[tokio::test(flavor = "multi_thread")] async fn test_migration_step_2() -> Result<(), Box> { + use crate::migration::plaintext_to_awsdbe::plaintext::migration_step_0::migration_step_0_example; + use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_2::migration_step_2_example; + use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_3::migration_step_3_example; + use crate::test_utils; + use uuid::Uuid; + let kms_key_id = test_utils::TEST_KMS_KEY_ID; let table_name = test_utils::TEST_DDB_TABLE_NAME; let partition_key = Uuid::new_v4().to_string(); diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_3.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_3.rs index 2284f6ac5..014e349dc 100644 --- a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_3.rs +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_3.rs @@ -9,12 +9,6 @@ use crate::migration::plaintext_to_awsdbe::migration_utils::{ verify_returned_item, ENCRYPTED_AND_SIGNED_VALUE, SIGN_ONLY_VALUE, DO_NOTHING_VALUE, }; use crate::migration::plaintext_to_awsdbe::awsdbe::common::create_table_configs; -// We import these packages for testing combination of migration steps -use crate::migration::plaintext_to_awsdbe::plaintext::migration_step_0::migration_step_0_example; -use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_1::migration_step_1_example; -use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_2::migration_step_2_example; -use crate::test_utils; -use uuid::Uuid; /* Migration Step 3: This is the final step in the migration process from @@ -146,6 +140,12 @@ pub async fn migration_step_3_example( #[tokio::test(flavor = "multi_thread")] async fn test_migration_step_3() -> Result<(), Box> { + use crate::migration::plaintext_to_awsdbe::plaintext::migration_step_0::migration_step_0_example; + use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_2::migration_step_2_example; + use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_3::migration_step_3_example; + use crate::test_utils; + use uuid::Uuid; + let kms_key_id = test_utils::TEST_KMS_KEY_ID; let table_name = test_utils::TEST_DDB_TABLE_NAME; let partition_key = Uuid::new_v4().to_string(); diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/plaintext/migration_step_0.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/plaintext/migration_step_0.rs index ac360254b..48c92a7a4 100644 --- a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/plaintext/migration_step_0.rs +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/plaintext/migration_step_0.rs @@ -6,12 +6,6 @@ use std::collections::HashMap; use crate::migration::plaintext_to_awsdbe::migration_utils::{ verify_returned_item, ENCRYPTED_AND_SIGNED_VALUE, SIGN_ONLY_VALUE, DO_NOTHING_VALUE, }; -// We import these packages for testing combination of migration steps -use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_1::migration_step_1_example; -use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_2::migration_step_2_example; -use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_3::migration_step_3_example; -use crate::test_utils; -use uuid::Uuid; /* Migration Step 0: This is the pre-migration step for the @@ -118,6 +112,12 @@ pub async fn migration_step_0_example( #[tokio::test(flavor = "multi_thread")] async fn test_migration_step_0() -> Result<(), Box> { + use crate::migration::plaintext_to_awsdbe::plaintext::migration_step_0::migration_step_0_example; + use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_2::migration_step_2_example; + use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_3::migration_step_3_example; + use crate::test_utils; + use uuid::Uuid; + let kms_key_id = test_utils::TEST_KMS_KEY_ID; let table_name = test_utils::TEST_DDB_TABLE_NAME; let partition_key = Uuid::new_v4().to_string(); From e85d7e20d211856de218672844d2711f42e370f9 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Thu, 28 Aug 2025 15:36:03 -0700 Subject: [PATCH 12/15] auto commit --- .../migration/plaintext_to_awsdbe/awsdbe/migration_step_2.rs | 2 +- .../migration/plaintext_to_awsdbe/awsdbe/migration_step_3.rs | 2 +- .../migration/plaintext_to_awsdbe/plaintext/migration_step_0.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_2.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_2.rs index 6a862451a..e15be9659 100644 --- a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_2.rs +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_2.rs @@ -140,7 +140,7 @@ pub async fn migration_step_2_example( #[tokio::test(flavor = "multi_thread")] async fn test_migration_step_2() -> Result<(), Box> { use crate::migration::plaintext_to_awsdbe::plaintext::migration_step_0::migration_step_0_example; - use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_2::migration_step_2_example; + use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_1::migration_step_1_example; use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_3::migration_step_3_example; use crate::test_utils; use uuid::Uuid; diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_3.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_3.rs index 014e349dc..94f433a8d 100644 --- a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_3.rs +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_3.rs @@ -141,8 +141,8 @@ pub async fn migration_step_3_example( #[tokio::test(flavor = "multi_thread")] async fn test_migration_step_3() -> Result<(), Box> { use crate::migration::plaintext_to_awsdbe::plaintext::migration_step_0::migration_step_0_example; + use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_1::migration_step_1_example; use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_2::migration_step_2_example; - use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_3::migration_step_3_example; use crate::test_utils; use uuid::Uuid; diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/plaintext/migration_step_0.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/plaintext/migration_step_0.rs index 48c92a7a4..d69b97508 100644 --- a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/plaintext/migration_step_0.rs +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/plaintext/migration_step_0.rs @@ -112,7 +112,7 @@ pub async fn migration_step_0_example( #[tokio::test(flavor = "multi_thread")] async fn test_migration_step_0() -> Result<(), Box> { - use crate::migration::plaintext_to_awsdbe::plaintext::migration_step_0::migration_step_0_example; + use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_1::migration_step_1_example; use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_2::migration_step_2_example; use crate::migration::plaintext_to_awsdbe::awsdbe::migration_step_3::migration_step_3_example; use crate::test_utils; From 140f3c0771777fd0ed348437ce1318f42abc2512 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Fri, 29 Aug 2025 09:10:02 -0700 Subject: [PATCH 13/15] auto commit --- .../awsdbe/migration_step_1.rs | 8 +++---- .../awsdbe/migration_step_2.rs | 16 ++++++------- .../awsdbe/migration_step_3.rs | 24 +++++++++---------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_1.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_1.rs index 7c1eac649..efcefed8e 100644 --- a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_1.rs +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_1.rs @@ -147,15 +147,15 @@ async fn test_migration_step_1() -> Result<(), Box> { let table_name = test_utils::TEST_DDB_TABLE_NAME; let partition_key = Uuid::new_v4().to_string(); let sort_keys = ["0", "1", "2", "3"]; + + // Successfully executes step 1 + let success = migration_step_1_example(kms_key_id, table_name, &partition_key, sort_keys[1], sort_keys[1]).await?; + assert!(success, "MigrationStep1 should complete successfully"); // Given: Step 0 has succeeded let success = migration_step_0_example(table_name, &partition_key, sort_keys[0], sort_keys[0]).await?; assert!(success, "MigrationStep0 should complete successfully"); - // Successfully executes step 1 - let success = migration_step_1_example(kms_key_id, table_name, &partition_key, sort_keys[1], sort_keys[1]).await?; - assert!(success, "MigrationStep1 should complete successfully"); - // When: Execute Step 1 with sortReadValue=0, Then: Success (i.e. can read plaintext values from Step 0) let success = migration_step_1_example(kms_key_id, table_name, &partition_key, sort_keys[1], sort_keys[0]).await?; assert!(success, "MigrationStep1 should be able to read items written by Step 0"); diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_2.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_2.rs index e15be9659..52872dac0 100644 --- a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_2.rs +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_2.rs @@ -150,22 +150,22 @@ async fn test_migration_step_2() -> Result<(), Box> { let partition_key = Uuid::new_v4().to_string(); let sort_keys = ["0", "1", "2", "3"]; - // Given: Step 0 has succeeded - let success = migration_step_0_example(table_name, &partition_key, sort_keys[0], sort_keys[0]).await?; - assert!(success, "MigrationStep0 should complete successfully"); - - // Given: Step 1 has succeeded - let success = migration_step_1_example(kms_key_id, table_name, &partition_key, sort_keys[1], sort_keys[1]).await?; - assert!(success, "MigrationStep1 should complete successfully"); - // Successfully executes step 2 let success = migration_step_2_example(kms_key_id, table_name, &partition_key, sort_keys[2], sort_keys[2]).await?; assert!(success, "MigrationStep2 should complete successfully"); + // Given: Step 0 has succeeded + let success = migration_step_0_example(table_name, &partition_key, sort_keys[0], sort_keys[0]).await?; + assert!(success, "MigrationStep0 should complete successfully"); + // When: Execute Step 2 with sortReadValue=0, Then: Success (i.e. can read plaintext values from Step 0) let success = migration_step_2_example(kms_key_id, table_name, &partition_key, sort_keys[2], sort_keys[0]).await?; assert!(success, "MigrationStep2 should be able to read items written by Step 0"); + // Given: Step 1 has succeeded + let success = migration_step_1_example(kms_key_id, table_name, &partition_key, sort_keys[1], sort_keys[1]).await?; + assert!(success, "MigrationStep1 should complete successfully"); + // When: Execute Step 2 with sortReadValue=1, Then: Success (i.e. can read plaintext values from Step 1) let success = migration_step_2_example(kms_key_id, table_name, &partition_key, sort_keys[2], sort_keys[1]).await?; assert!(success, "MigrationStep2 should be able to read items written by Step 1"); diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_3.rs b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_3.rs index 94f433a8d..c54cba21d 100644 --- a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_3.rs +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/awsdbe/migration_step_3.rs @@ -151,30 +151,30 @@ async fn test_migration_step_3() -> Result<(), Box> { let partition_key = Uuid::new_v4().to_string(); let sort_keys = ["0", "1", "2", "3"]; + // Successfully executes step 3 + let success = migration_step_3_example(kms_key_id, table_name, &partition_key, sort_keys[3], sort_keys[3]).await?; + assert!(success, "MigrationStep3 should complete successfully"); + // Given: Step 0 has succeeded let success = migration_step_0_example(table_name, &partition_key, sort_keys[0], sort_keys[0]).await?; assert!(success, "MigrationStep0 should complete successfully"); - // Given: Step 1 has succeeded - let success = migration_step_1_example(kms_key_id, table_name, &partition_key, sort_keys[1], sort_keys[1]).await?; - assert!(success, "MigrationStep1 should complete successfully"); - - // Given: Step 2 has succeeded - let success = migration_step_2_example(kms_key_id, table_name, &partition_key, sort_keys[2], sort_keys[2]).await?; - assert!(success, "MigrationStep2 should complete successfully"); - - // Successfully executes step 3 - let success = migration_step_3_example(kms_key_id, table_name, &partition_key, sort_keys[3], sort_keys[3]).await?; - assert!(success, "MigrationStep3 should complete successfully"); - // When: Execute Step 3 with sortReadValue=0, Then: should error out when reading plaintext items from Step 0 let result = migration_step_3_example(kms_key_id, table_name, &partition_key, sort_keys[3], sort_keys[0]).await; assert!(result.is_err(), "MigrationStep3 should fail when reading plaintext items"); + // Given: Step 1 has succeeded + let success = migration_step_1_example(kms_key_id, table_name, &partition_key, sort_keys[1], sort_keys[1]).await?; + assert!(success, "MigrationStep1 should complete successfully"); + // When: Execute Step 3 with sortReadValue=1, Then: should error out when reading plaintext items from Step 1 let result = migration_step_3_example(kms_key_id, table_name, &partition_key, sort_keys[3], sort_keys[1]).await; assert!(result.is_err(), "MigrationStep3 should fail when reading plaintext items"); + // Given: Step 2 has succeeded + let success = migration_step_2_example(kms_key_id, table_name, &partition_key, sort_keys[2], sort_keys[2]).await?; + assert!(success, "MigrationStep2 should complete successfully"); + // When: Execute Step 3 with sortReadValue=2, Then: Success (i.e. can read encrypted values from Step 2) let success = migration_step_3_example(kms_key_id, table_name, &partition_key, sort_keys[3], sort_keys[2]).await?; assert!(success, "MigrationStep3 should be able to read items written by Step 2"); From 1c4a0b9c5dacad4b0b2c953b7b4e00f07f758cdb Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Fri, 29 Aug 2025 09:17:06 -0700 Subject: [PATCH 14/15] auto commit --- .../migration/plaintext_to_awsdbe/README.md | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/README.md diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/README.md b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/README.md new file mode 100644 index 000000000..c0fd2b0e5 --- /dev/null +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/README.md @@ -0,0 +1,51 @@ +# Plaintext DynamoDB Table to AWS Database Encryption SDK Encrypted Table Migration + +This projects demonstrates the steps necessary +to migrate to the AWS Database Encryption SDK for DynamoDb +from a plaintext database. + +[Step 0](plaintext/step0.go) demonstrates the starting state for your system. + +## Step 1 + +In Step 1, you update your system to do the following: + +- continue to read plaintext items +- continue to write plaintext items +- prepare to read encrypted items + +When you deploy changes in Step 1, +you should not expect any behavior change in your system, +and your dataset still consists of plaintext data. + +You must ensure that the changes in Step 1 make it to all your readers before you proceed to Step 2. + +## Step 2 + +In Step 2, you update your system to do the following: + +- continue to read plaintext items +- start writing encrypted items +- continue to read encrypted items + +When you deploy changes in Step 2, +you are introducing encrypted items to your system, +and must make sure that all your readers are updated with the changes from Step 1. + +Before you move onto the next step, you will need to encrypt all plaintext items in your dataset. +Once you have completed this step, +while new items are being encrypted using the new format and will be authenticated on read, +your system will still accept reading plaintext, unauthenticated items. +In order to complete migration to a system where you always authenticate your items, +you should prioritize moving on to Step 3. + +## Step 3 + +Once all old items are encrypted, +update your system to do the following: + +- continue to write encrypted items +- continue to read encrypted items +- do not accept reading plaintext items + +Once you have deployed these changes to your system, you have completed migration. \ No newline at end of file From 475a43c5ebfe17d5060a3984f6a18b6fdc8441c5 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Fri, 29 Aug 2025 09:22:28 -0700 Subject: [PATCH 15/15] auto commit --- .../rust/examples/migration/plaintext_to_awsdbe/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/README.md b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/README.md index c0fd2b0e5..31170c101 100644 --- a/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/README.md +++ b/DynamoDbEncryption/runtimes/rust/examples/migration/plaintext_to_awsdbe/README.md @@ -48,4 +48,4 @@ update your system to do the following: - continue to read encrypted items - do not accept reading plaintext items -Once you have deployed these changes to your system, you have completed migration. \ No newline at end of file +Once you have deployed these changes to your system, you have completed migration.