From db455780aa1a75186b2d72e8cfddf5fd09fcbb1a Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Thu, 21 Aug 2025 10:33:43 -0700 Subject: [PATCH 01/25] 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 007a3437170b1330c67ad26d2ebca300ae2451c1 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Thu, 21 Aug 2025 16:01:13 -0700 Subject: [PATCH 02/25] auto commit --- .github/workflows/ci_examples_net.yml | 1 + .../PlaintextToAWSDBE/MigrationUtils.cs | 12 +- .../awsdbe/MigrationStep1.cs | 165 +++++++++++++++++- .../awsdbe/MigrationStep1Test.cs | 78 +++++++++ .../awsdbe/MigrationStep2.cs | 143 ++++++++++++++- .../awsdbe/MigrationStep2Test.cs | 78 +++++++++ .../awsdbe/MigrationStep3.cs | 144 ++++++++++++++- .../awsdbe/MigrationStep3Test.cs | 82 +++++++++ .../plaintext/MigrationStep0.cs | 8 +- .../plaintext/MigrationStep0Test.cs | 33 ++-- 10 files changed, 698 insertions(+), 46 deletions(-) create mode 100644 Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1Test.cs create mode 100644 Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2Test.cs create mode 100644 Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3Test.cs diff --git a/.github/workflows/ci_examples_net.yml b/.github/workflows/ci_examples_net.yml index f9d85e050..41d90d49c 100644 --- a/.github/workflows/ci_examples_net.yml +++ b/.github/workflows/ci_examples_net.yml @@ -94,3 +94,4 @@ jobs: shell: bash run: | dotnet run + dotnet test diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/MigrationUtils.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/MigrationUtils.cs index 4fb02afc7..1358842ac 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/MigrationUtils.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/MigrationUtils.cs @@ -10,10 +10,20 @@ This class contains shared functionality used by all migration steps. */ public class MigrationUtils { + // Common attribute values used across all migration steps + public static readonly string ENCRYPTED_AND_SIGNED_VALUE = "this will be encrypted and signed"; + public static readonly string SIGN_ONLY_VALUE = "this will never be encrypted, but it will be signed"; + public static readonly string DO_NOTHING_VALUE = "this will never be encrypted nor signed"; + // 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) + string encryptedAndSignedValue = null, string signOnlyValue = null, string doNothingValue = null) { + // Use default values if not provided + encryptedAndSignedValue = encryptedAndSignedValue ?? ENCRYPTED_AND_SIGNED_VALUE; + signOnlyValue = signOnlyValue ?? SIGN_ONLY_VALUE; + doNothingValue = doNothingValue ?? DO_NOTHING_VALUE; + var item = response.Item; // Verify partition key diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs index 84e7789b0..1b0e6ebd5 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs @@ -16,25 +16,174 @@ 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 + 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 (S) + - Sort key is named "sort_key" with type (N) */ 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"); + try + { + // 1. 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. + var matProv = new MaterialProviders(new MaterialProvidersConfig()); + var keyringInput = new CreateAwsKmsMrkMultiKeyringInput { Generator = kmsKeyId }; + var kmsKeyring = matProv.CreateAwsKmsMrkMultiKeyring(keyringInput); + + // 2. 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 + var attributeActionsOnEncrypt = new Dictionary + { + ["partition_key"] = CryptoAction.SIGN_ONLY, // Our partition attribute must be SIGN_ONLY + ["sort_key"] = CryptoAction.SIGN_ONLY, // Our sort attribute must be SIGN_ONLY + ["attribute1"] = CryptoAction.ENCRYPT_AND_SIGN, + ["attribute2"] = CryptoAction.SIGN_ONLY, + ["attribute3"] = CryptoAction.DO_NOTHING + }; + + // 3. 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. + var unsignedAttributes = new List { "attribute3" }; + + // 4. Create the DynamoDb Encryption configuration for the table we will be writing to. + // This configuration uses PlaintextOverride.FORCE_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ + // 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. + var tableConfig = new DynamoDbTableEncryptionConfig + { + LogicalTableName = ddbTableName, + PartitionKeyName = "partition_key", + SortKeyName = "sort_key", + AttributeActionsOnEncrypt = attributeActionsOnEncrypt, + Keyring = kmsKeyring, + AllowedUnsignedAttributes = unsignedAttributes, + PlaintextOverride = PlaintextOverride.FORCE_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ + }; + + var tableConfigs = new Dictionary + { + [ddbTableName] = tableConfig + }; + + // 5. Create a new AWS SDK DynamoDb client using the TableEncryptionConfigs + var ddb = new Client.DynamoDbClient( + new DynamoDbTablesEncryptionConfig { TableEncryptionConfigs = tableConfigs }); + + // 6. Put an item into our table using the above client. + // This item will be stored in plaintext due to our PlaintextOverride configuration. + string encryptedAndSignedValue = MigrationUtils.ENCRYPTED_AND_SIGNED_VALUE; + string signOnlyValue = MigrationUtils.SIGN_ONLY_VALUE; + string doNothingValue = MigrationUtils.DO_NOTHING_VALUE; + 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); + + // 7. 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. + var key = new Dictionary + { + ["partition_key"] = new AttributeValue { S = partitionKeyValue }, + ["sort_key"] = new AttributeValue { N = sortKeyReadValue } + }; + + var getRequest = new GetItemRequest + { + TableName = ddbTableName, + Key = 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. + ConsistentRead = true + }; + + var getResponse = await ddb.GetItemAsync(getRequest); + Debug.Assert(getResponse.HttpStatusCode == HttpStatusCode.OK); + + // 8. Verify we get the expected item back + if (getResponse.Item == null) + { + throw new Exception("No item found"); + } + + bool success = MigrationUtils.VerifyReturnedItem(getResponse, partitionKeyValue, sortKeyReadValue); + if (success) + { + Console.WriteLine("MigrationStep1 completed successfully"); + } + return success; + } + catch (Exception e) + { + Console.WriteLine($"Error in MigrationStep1: {e.Message}"); + throw; + } } } } diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1Test.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1Test.cs new file mode 100644 index 000000000..f4b54d479 --- /dev/null +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1Test.cs @@ -0,0 +1,78 @@ +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; +using Examples.migration.PlaintextToAWSDBE.plaintext; + +namespace Examples.migration.PlaintextToAWSDBE.awsdbe +{ + /* + Test for Migration Step 1: This tests the first step in the + plaintext-to-encrypted database migration. + + This test verifies that: + 1. Step 1 can successfully write plaintext items + 2. Step 1 can read items written by Step 0 (plaintext) + 3. Step 1 can read items written by itself (plaintext) + 4. Step 1 can read items written by Step 2 (encrypted) + 5. Step 1 can read items written by Step 3 (encrypted) + + 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 (N) + */ + public class MigrationStep1Test + { + [Fact] + public async Task TestMigrationStep1() + { + 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 + { + // Given: Step 0 has succeeded + bool success = await MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[0]); + Assert.True(success, "MigrationStep0 should complete successfully"); + + // Successfully executes step 1 + success = await MigrationStep1.MigrationStep1Example(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[1]); + Assert.True(success, "MigrationStep1 should complete successfully"); + + // When: Execute Step 1 with sortReadValue=0, Then: Success (i.e. can read plaintext values from Step 0) + success = await MigrationStep1.MigrationStep1Example(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[0]); + Assert.True(success, "MigrationStep1 should be able to read items written by Step 0"); + + // Given: Step 2 has succeeded + success = await MigrationStep2.MigrationStep2Example(kmsKeyID, tableName, partitionKey, sortKeys[2], sortKeys[2]); + Assert.True(success, "MigrationStep2 should complete successfully"); + + // When: Execute Step 1 with sortReadValue=2, Then: Success (i.e. can read encrypted values from Step 2) + success = await MigrationStep1.MigrationStep1Example(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[2]); + Assert.True(success, "MigrationStep1 should be able to read items written by Step 2"); + + // Given: Step 3 has succeeded + success = await MigrationStep3.MigrationStep3Example(kmsKeyID, tableName, partitionKey, sortKeys[3], sortKeys[3]); + Assert.True(success, "MigrationStep3 should complete successfully"); + + // When: Execute Step 1 with sortReadValue=3, Then: Success (i.e. can read encrypted values from Step 3) + success = await MigrationStep1.MigrationStep1Example(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[3]); + Assert.True(success, "MigrationStep1 should be able to read items written by Step 3"); + } + finally + { + // Cleanup + await MigrationUtils.CleanupItems(tableName, partitionKey, sortKeys); + } + } + } +} diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2.cs index 090c43404..e8722fbdd 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2.cs @@ -16,25 +16,152 @@ 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 + 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 (S) + - Sort key is named "sort_key" with type (N) */ 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"); + try + { + // 1. 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. + var matProv = new MaterialProviders(new MaterialProvidersConfig()); + var keyringInput = new CreateAwsKmsMrkMultiKeyringInput { Generator = kmsKeyId }; + var kmsKeyring = matProv.CreateAwsKmsMrkMultiKeyring(keyringInput); + + // 2. 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 + var attributeActionsOnEncrypt = new Dictionary + { + ["partition_key"] = CryptoAction.SIGN_ONLY, // Our partition attribute must be SIGN_ONLY + ["sort_key"] = CryptoAction.SIGN_ONLY, // Our sort attribute must be SIGN_ONLY + ["attribute1"] = CryptoAction.ENCRYPT_AND_SIGN, + ["attribute2"] = CryptoAction.SIGN_ONLY, + ["attribute3"] = CryptoAction.DO_NOTHING + }; + + // 3. Configure which attributes we expect to be excluded in the signature + // when reading items. + // For this example, we will explicitly list the attributes that are not signed. + var unsignedAttributes = new List { "attribute3" }; + + // 4. Create the DynamoDb Encryption configuration for the table we will be writing to. + // This configuration uses PlaintextOverride.FORBID_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ + // 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. + var tableConfig = new DynamoDbTableEncryptionConfig + { + LogicalTableName = ddbTableName, + PartitionKeyName = "partition_key", + SortKeyName = "sort_key", + AttributeActionsOnEncrypt = attributeActionsOnEncrypt, + Keyring = kmsKeyring, + AllowedUnsignedAttributes = unsignedAttributes, + PlaintextOverride = PlaintextOverride.FORBID_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ + }; + + var tableConfigs = new Dictionary + { + [ddbTableName] = tableConfig + }; + + // 5. Create a new AWS SDK DynamoDb client using the TableEncryptionConfigs + var ddb = new Client.DynamoDbClient( + new DynamoDbTablesEncryptionConfig { TableEncryptionConfigs = tableConfigs }); + + // 6. Put an item into our table using the above client. + // This item will be encrypted due to our PlaintextOverride configuration. + string encryptedAndSignedValue = MigrationUtils.ENCRYPTED_AND_SIGNED_VALUE; + string signOnlyValue = MigrationUtils.SIGN_ONLY_VALUE; + string doNothingValue = MigrationUtils.DO_NOTHING_VALUE; + 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); + + // 7. 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. + var key = new Dictionary + { + ["partition_key"] = new AttributeValue { S = partitionKeyValue }, + ["sort_key"] = new AttributeValue { N = sortKeyReadValue } + }; + + var getRequest = new GetItemRequest + { + TableName = ddbTableName, + Key = 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. + ConsistentRead = true + }; + + var getResponse = await ddb.GetItemAsync(getRequest); + Debug.Assert(getResponse.HttpStatusCode == HttpStatusCode.OK); + + // 8. Verify we get the expected item back + if (getResponse.Item == null) + { + throw new Exception("No item found"); + } + + bool success = MigrationUtils.VerifyReturnedItem(getResponse, partitionKeyValue, sortKeyReadValue); + if (success) + { + Console.WriteLine("MigrationStep2 completed successfully"); + } + return success; + } + catch (Exception e) + { + Console.WriteLine($"Error in MigrationStep2: {e.Message}"); + throw; + } } } } diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2Test.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2Test.cs new file mode 100644 index 000000000..0d855ec2c --- /dev/null +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2Test.cs @@ -0,0 +1,78 @@ +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; +using Examples.migration.PlaintextToAWSDBE.plaintext; + +namespace Examples.migration.PlaintextToAWSDBE.awsdbe +{ + /* + Test for Migration Step 2: This tests the second step in the + plaintext-to-encrypted database migration. + + This test verifies that: + 1. Step 2 can successfully write encrypted items + 2. Step 2 can read items written by Step 0 (plaintext) + 3. Step 2 can read items written by Step 1 (plaintext) + 4. Step 2 can read items written by itself (encrypted) + 5. Step 2 can read items written by Step 3 (encrypted) + + 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 (N) + */ + public class MigrationStep2Test + { + [Fact] + public async Task TestMigrationStep2() + { + 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 + { + // Given: Step 0 has succeeded + bool success = await MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[0]); + Assert.True(success, "MigrationStep0 should complete successfully"); + + // Given: Step 1 has succeeded + success = await MigrationStep1.MigrationStep1Example(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[1]); + Assert.True(success, "MigrationStep1 should complete successfully"); + + // Successfully executes step 2 + success = await MigrationStep2.MigrationStep2Example(kmsKeyID, tableName, partitionKey, sortKeys[2], sortKeys[2]); + Assert.True(success, "MigrationStep2 should complete successfully"); + + // When: Execute Step 2 with sortReadValue=0, Then: Success (i.e. can read plaintext values from Step 0) + success = await MigrationStep2.MigrationStep2Example(kmsKeyID, tableName, partitionKey, sortKeys[2], sortKeys[0]); + Assert.True(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) + success = await MigrationStep2.MigrationStep2Example(kmsKeyID, tableName, partitionKey, sortKeys[2], sortKeys[1]); + Assert.True(success, "MigrationStep2 should be able to read items written by Step 1"); + + // Given: Step 3 has succeeded + success = await MigrationStep3.MigrationStep3Example(kmsKeyID, tableName, partitionKey, sortKeys[3], sortKeys[3]); + Assert.True(success, "MigrationStep3 should complete successfully"); + + // When: Execute Step 2 with sortReadValue=3, Then: Success (i.e. can read encrypted values from Step 3) + success = await MigrationStep2.MigrationStep2Example(kmsKeyID, tableName, partitionKey, sortKeys[2], sortKeys[3]); + Assert.True(success, "MigrationStep2 should be able to read items written by Step 3"); + } + finally + { + // Cleanup + await MigrationUtils.CleanupItems(tableName, partitionKey, sortKeys); + } + } + } +} diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3.cs index c978eeddb..1729d038c 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3.cs @@ -16,25 +16,153 @@ 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 + 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 (S) + - Sort key is named "sort_key" with type (N) */ 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"); + try + { + // 1. 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. + var matProv = new MaterialProviders(new MaterialProvidersConfig()); + var keyringInput = new CreateAwsKmsMrkMultiKeyringInput { Generator = kmsKeyId }; + var kmsKeyring = matProv.CreateAwsKmsMrkMultiKeyring(keyringInput); + + // 2. 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 + var attributeActionsOnEncrypt = new Dictionary + { + ["partition_key"] = CryptoAction.SIGN_ONLY, // Our partition attribute must be SIGN_ONLY + ["sort_key"] = CryptoAction.SIGN_ONLY, // Our sort attribute must be SIGN_ONLY + ["attribute1"] = CryptoAction.ENCRYPT_AND_SIGN, + ["attribute2"] = CryptoAction.SIGN_ONLY, + ["attribute3"] = CryptoAction.DO_NOTHING + }; + + // 3. Configure which attributes we expect to be excluded in the signature + // when reading items. + // For this example, we will explicitly list the attributes that are not signed. + var unsignedAttributes = new List { "attribute3" }; + + // 4. Create the DynamoDb Encryption configuration for the table we will be writing to. + // This configuration uses PlaintextOverride.FORBID_PLAINTEXT_WRITE_FORBID_PLAINTEXT_READ + // 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 + // FORBID_PLAINTEXT_WRITE_FORBID_PLAINTEXT_READ, which is the desired + // behavior for a client interacting with a fully encrypted database. + var tableConfig = new DynamoDbTableEncryptionConfig + { + LogicalTableName = ddbTableName, + PartitionKeyName = "partition_key", + SortKeyName = "sort_key", + AttributeActionsOnEncrypt = attributeActionsOnEncrypt, + Keyring = kmsKeyring, + AllowedUnsignedAttributes = unsignedAttributes, + PlaintextOverride = PlaintextOverride.FORBID_PLAINTEXT_WRITE_FORBID_PLAINTEXT_READ + }; + + var tableConfigs = new Dictionary + { + [ddbTableName] = tableConfig + }; + + // 5. Create a new AWS SDK DynamoDb client using the TableEncryptionConfigs + var ddb = new Client.DynamoDbClient( + new DynamoDbTablesEncryptionConfig { TableEncryptionConfigs = tableConfigs }); + + // 6. Put an item into our table using the above client. + // This item will be encrypted due to our PlaintextOverride configuration. + string encryptedAndSignedValue = MigrationUtils.ENCRYPTED_AND_SIGNED_VALUE; + string signOnlyValue = MigrationUtils.SIGN_ONLY_VALUE; + string doNothingValue = MigrationUtils.DO_NOTHING_VALUE; + 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); + + // 7. 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. + var key = new Dictionary + { + ["partition_key"] = new AttributeValue { S = partitionKeyValue }, + ["sort_key"] = new AttributeValue { N = sortKeyReadValue } + }; + + var getRequest = new GetItemRequest + { + TableName = ddbTableName, + Key = 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. + ConsistentRead = true + }; + + var getResponse = await ddb.GetItemAsync(getRequest); + Debug.Assert(getResponse.HttpStatusCode == HttpStatusCode.OK); + + // 8. Verify we get the expected item back + if (getResponse.Item == null) + { + throw new Exception("No item found"); + } + + bool success = MigrationUtils.VerifyReturnedItem(getResponse, partitionKeyValue, sortKeyReadValue); + if (success) + { + Console.WriteLine("MigrationStep3 completed successfully"); + } + return success; + } + catch (Exception e) + { + Console.WriteLine($"Error in MigrationStep3: {e.Message}"); + throw; + } } } } diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3Test.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3Test.cs new file mode 100644 index 000000000..ce8b7ee37 --- /dev/null +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3Test.cs @@ -0,0 +1,82 @@ +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; +using Examples.migration.PlaintextToAWSDBE.plaintext; +using AWS.Cryptography.DbEncryptionSDK.DynamoDb.ItemEncryptor; + +namespace Examples.migration.PlaintextToAWSDBE.awsdbe +{ + /* + Test for Migration Step 3: This tests the final step in the + plaintext-to-encrypted database migration. + + This test verifies that: + 1. Step 3 can successfully write encrypted items + 2. Step 3 cannot read items written by Step 0 (plaintext) - should throw an exception + 3. Step 3 cannot read items written by Step 1 (plaintext) - should throw an exception + 4. Step 3 can read items written by Step 2 (encrypted) + 5. Step 3 can read items written by itself (encrypted) + + 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 (N) + */ + public class MigrationStep3Test + { + [Fact] + public async Task TestMigrationStep3() + { + 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 + { + // Given: Step 0 has succeeded + bool success = await MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[0]); + Assert.True(success, "MigrationStep0 should complete successfully"); + + // Given: Step 1 has succeeded + success = await MigrationStep1.MigrationStep1Example(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[1]); + Assert.True(success, "MigrationStep1 should complete successfully"); + + // Given: Step 2 has succeeded + success = await MigrationStep2.MigrationStep2Example(kmsKeyID, tableName, partitionKey, sortKeys[2], sortKeys[2]); + Assert.True(success, "MigrationStep2 should complete successfully"); + + // Successfully executes step 3 + success = await MigrationStep3.MigrationStep3Example(kmsKeyID, tableName, partitionKey, sortKeys[3], sortKeys[3]); + Assert.True(success, "MigrationStep3 should complete successfully"); + + // When: Execute Step 3 with sortReadValue=0, Then: should error out when reading plaintext items from Step 0 + var exception = await Assert.ThrowsAsync(() => + MigrationStep3.MigrationStep3Example(kmsKeyID, tableName, partitionKey, sortKeys[3], sortKeys[0])); + + Assert.Contains("encrypted item missing expected header and footer attributes", exception.Message.ToLower()); + + // When: Execute Step 3 with sortReadValue=1, Then: should error out when reading plaintext items from Step 1 + exception = await Assert.ThrowsAsync(() => + MigrationStep3.MigrationStep3Example(kmsKeyID, tableName, partitionKey, sortKeys[3], sortKeys[1])); + Assert.Contains("encrypted item missing expected header and footer attributes", exception.Message.ToLower()); + + // When: Execute Step 3 with sortReadValue=2, Then: Success (i.e. can read encrypted values from Step 2) + success = await MigrationStep3.MigrationStep3Example(kmsKeyID, tableName, partitionKey, sortKeys[3], sortKeys[2]); + Assert.True(success, "MigrationStep3 should be able to read items written by Step 2"); + } + finally + { + // Cleanup + await MigrationUtils.CleanupItems(tableName, partitionKey, sortKeys); + } + } + } +} diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0.cs index 215d9b028..3727fa8d2 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0.cs @@ -40,9 +40,9 @@ public static async Task MigrationStep0Example(string ddbTableName, string // 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"; + string encryptedAndSignedValue = MigrationUtils.ENCRYPTED_AND_SIGNED_VALUE; + string signOnlyValue = MigrationUtils.SIGN_ONLY_VALUE; + string doNothingValue = MigrationUtils.DO_NOTHING_VALUE; var item = new Dictionary { ["partition_key"] = new AttributeValue { S = partitionKeyValue }, @@ -91,7 +91,7 @@ public static async Task MigrationStep0Example(string ddbTableName, string throw new Exception("No item found"); } - bool success = MigrationUtils.VerifyReturnedItem(getResponse, partitionKeyValue, sortKeyReadValue, encryptedAndSignedValue, signOnlyValue, doNothingValue); + bool success = MigrationUtils.VerifyReturnedItem(getResponse, partitionKeyValue, sortKeyReadValue); if (success) { Console.WriteLine("MigrationStep0 completed successfully"); diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0Test.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0Test.cs index 5e23bf75f..5382748e7 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0Test.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0Test.cs @@ -15,15 +15,15 @@ namespace Examples.migration.PlaintextToAWSDBE.plaintext 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) + 2. Step 0 can read items written by Step 1 (which are also plaintext) + 3. Step 0 cannot read items written by Steps 2 and 3 (which are encrypted) 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) + - Sort key is named "sort_key" with type (N) */ public class MigrationStep0Test { @@ -41,30 +41,29 @@ public async Task TestMigrationStep0() 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]); + 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"); + 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]); + 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); + var exception = await Assert.ThrowsAsync(() => + MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[2])); + + Assert.Contains("attribute1 mismatch", exception.Message); - // Given: Step 3 has succeeded (if it exists) - // await awsdbe.MigrationStep3.MigrationStep3Example(kmsKeyID, tableName, partitionKey, sortKeys[3], sortKeys[3]); + // Given: Step 3 has succeeded + 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); + exception = await Assert.ThrowsAsync(() => + MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[3])); + Assert.Contains("attribute1 mismatch", exception.Message); } finally { From aadb18ebca2cbf9976cdc07984ace247e8695d22 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Thu, 21 Aug 2025 16:05:26 -0700 Subject: [PATCH 03/25] auto commit --- .../awsdbe/MigrationStep1Test.cs | 53 ++--- .../awsdbe/MigrationStep2.cs | 208 ++++++++--------- .../awsdbe/MigrationStep2Test.cs | 53 ++--- .../awsdbe/MigrationStep3.cs | 216 +++++++++--------- .../awsdbe/MigrationStep3Test.cs | 63 +++-- .../plaintext/MigrationStep0.cs | 112 +++++---- .../plaintext/MigrationStep0Test.cs | 60 +++-- 7 files changed, 360 insertions(+), 405 deletions(-) diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1Test.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1Test.cs index f4b54d479..1e2f76a5e 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1Test.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1Test.cs @@ -38,41 +38,36 @@ public async Task TestMigrationStep1() string partitionKey = Guid.NewGuid().ToString(); string[] sortKeys = { "0", "1", "2", "3" }; - try - { - // Given: Step 0 has succeeded - bool success = await MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[0]); - Assert.True(success, "MigrationStep0 should complete successfully"); + // Given: Step 0 has succeeded + bool success = await MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[0]); + Assert.True(success, "MigrationStep0 should complete successfully"); - // Successfully executes step 1 - success = await MigrationStep1.MigrationStep1Example(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[1]); - Assert.True(success, "MigrationStep1 should complete successfully"); + // Successfully executes step 1 + success = await MigrationStep1.MigrationStep1Example(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[1]); + Assert.True(success, "MigrationStep1 should complete successfully"); - // When: Execute Step 1 with sortReadValue=0, Then: Success (i.e. can read plaintext values from Step 0) - success = await MigrationStep1.MigrationStep1Example(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[0]); - Assert.True(success, "MigrationStep1 should be able to read items written by Step 0"); + // When: Execute Step 1 with sortReadValue=0, Then: Success (i.e. can read plaintext values from Step 0) + success = await MigrationStep1.MigrationStep1Example(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[0]); + Assert.True(success, "MigrationStep1 should be able to read items written by Step 0"); - // Given: Step 2 has succeeded - success = await MigrationStep2.MigrationStep2Example(kmsKeyID, tableName, partitionKey, sortKeys[2], sortKeys[2]); - Assert.True(success, "MigrationStep2 should complete successfully"); + // Given: Step 2 has succeeded + success = await MigrationStep2.MigrationStep2Example(kmsKeyID, tableName, partitionKey, sortKeys[2], sortKeys[2]); + Assert.True(success, "MigrationStep2 should complete successfully"); - // When: Execute Step 1 with sortReadValue=2, Then: Success (i.e. can read encrypted values from Step 2) - success = await MigrationStep1.MigrationStep1Example(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[2]); - Assert.True(success, "MigrationStep1 should be able to read items written by Step 2"); + // When: Execute Step 1 with sortReadValue=2, Then: Success (i.e. can read encrypted values from Step 2) + success = await MigrationStep1.MigrationStep1Example(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[2]); + Assert.True(success, "MigrationStep1 should be able to read items written by Step 2"); - // Given: Step 3 has succeeded - success = await MigrationStep3.MigrationStep3Example(kmsKeyID, tableName, partitionKey, sortKeys[3], sortKeys[3]); - Assert.True(success, "MigrationStep3 should complete successfully"); + // Given: Step 3 has succeeded + success = await MigrationStep3.MigrationStep3Example(kmsKeyID, tableName, partitionKey, sortKeys[3], sortKeys[3]); + Assert.True(success, "MigrationStep3 should complete successfully"); - // When: Execute Step 1 with sortReadValue=3, Then: Success (i.e. can read encrypted values from Step 3) - success = await MigrationStep1.MigrationStep1Example(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[3]); - Assert.True(success, "MigrationStep1 should be able to read items written by Step 3"); - } - finally - { - // Cleanup - await MigrationUtils.CleanupItems(tableName, partitionKey, sortKeys); - } + // When: Execute Step 1 with sortReadValue=3, Then: Success (i.e. can read encrypted values from Step 3) + success = await MigrationStep1.MigrationStep1Example(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[3]); + Assert.True(success, "MigrationStep1 should be able to read items written by Step 3"); + + // Cleanup + await MigrationUtils.CleanupItems(tableName, partitionKey, sortKeys); } } } diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2.cs index e8722fbdd..81ec855e6 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2.cs @@ -39,129 +39,121 @@ public class MigrationStep2 { public static async Task MigrationStep2Example(string kmsKeyId, string ddbTableName, string partitionKeyValue, string sortKeyWriteValue, string sortKeyReadValue) { - try - { - // 1. 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. - var matProv = new MaterialProviders(new MaterialProvidersConfig()); - var keyringInput = new CreateAwsKmsMrkMultiKeyringInput { Generator = kmsKeyId }; - var kmsKeyring = matProv.CreateAwsKmsMrkMultiKeyring(keyringInput); - - // 2. 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 - var attributeActionsOnEncrypt = new Dictionary - { - ["partition_key"] = CryptoAction.SIGN_ONLY, // Our partition attribute must be SIGN_ONLY - ["sort_key"] = CryptoAction.SIGN_ONLY, // Our sort attribute must be SIGN_ONLY - ["attribute1"] = CryptoAction.ENCRYPT_AND_SIGN, - ["attribute2"] = CryptoAction.SIGN_ONLY, - ["attribute3"] = CryptoAction.DO_NOTHING - }; + // 1. 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. + var matProv = new MaterialProviders(new MaterialProvidersConfig()); + var keyringInput = new CreateAwsKmsMrkMultiKeyringInput { Generator = kmsKeyId }; + var kmsKeyring = matProv.CreateAwsKmsMrkMultiKeyring(keyringInput); - // 3. Configure which attributes we expect to be excluded in the signature - // when reading items. - // For this example, we will explicitly list the attributes that are not signed. - var unsignedAttributes = new List { "attribute3" }; + // 2. 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 + var attributeActionsOnEncrypt = new Dictionary + { + ["partition_key"] = CryptoAction.SIGN_ONLY, // Our partition attribute must be SIGN_ONLY + ["sort_key"] = CryptoAction.SIGN_ONLY, // Our sort attribute must be SIGN_ONLY + ["attribute1"] = CryptoAction.ENCRYPT_AND_SIGN, + ["attribute2"] = CryptoAction.SIGN_ONLY, + ["attribute3"] = CryptoAction.DO_NOTHING + }; - // 4. Create the DynamoDb Encryption configuration for the table we will be writing to. - // This configuration uses PlaintextOverride.FORBID_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ - // 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. - var tableConfig = new DynamoDbTableEncryptionConfig - { - LogicalTableName = ddbTableName, - PartitionKeyName = "partition_key", - SortKeyName = "sort_key", - AttributeActionsOnEncrypt = attributeActionsOnEncrypt, - Keyring = kmsKeyring, - AllowedUnsignedAttributes = unsignedAttributes, - PlaintextOverride = PlaintextOverride.FORBID_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ - }; + // 3. Configure which attributes we expect to be excluded in the signature + // when reading items. + // For this example, we will explicitly list the attributes that are not signed. + var unsignedAttributes = new List { "attribute3" }; - var tableConfigs = new Dictionary - { - [ddbTableName] = tableConfig - }; + // 4. Create the DynamoDb Encryption configuration for the table we will be writing to. + // This configuration uses PlaintextOverride.FORBID_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ + // 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. + var tableConfig = new DynamoDbTableEncryptionConfig + { + LogicalTableName = ddbTableName, + PartitionKeyName = "partition_key", + SortKeyName = "sort_key", + AttributeActionsOnEncrypt = attributeActionsOnEncrypt, + Keyring = kmsKeyring, + AllowedUnsignedAttributes = unsignedAttributes, + PlaintextOverride = PlaintextOverride.FORBID_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ + }; - // 5. Create a new AWS SDK DynamoDb client using the TableEncryptionConfigs - var ddb = new Client.DynamoDbClient( - new DynamoDbTablesEncryptionConfig { TableEncryptionConfigs = tableConfigs }); + var tableConfigs = new Dictionary + { + [ddbTableName] = tableConfig + }; - // 6. Put an item into our table using the above client. - // This item will be encrypted due to our PlaintextOverride configuration. - string encryptedAndSignedValue = MigrationUtils.ENCRYPTED_AND_SIGNED_VALUE; - string signOnlyValue = MigrationUtils.SIGN_ONLY_VALUE; - string doNothingValue = MigrationUtils.DO_NOTHING_VALUE; - 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 } - }; + // 5. Create a new AWS SDK DynamoDb client using the TableEncryptionConfigs + var ddb = new Client.DynamoDbClient( + new DynamoDbTablesEncryptionConfig { TableEncryptionConfigs = tableConfigs }); - var putRequest = new PutItemRequest - { - TableName = ddbTableName, - Item = item - }; + // 6. Put an item into our table using the above client. + // This item will be encrypted due to our PlaintextOverride configuration. + string encryptedAndSignedValue = MigrationUtils.ENCRYPTED_AND_SIGNED_VALUE; + string signOnlyValue = MigrationUtils.SIGN_ONLY_VALUE; + string doNothingValue = MigrationUtils.DO_NOTHING_VALUE; + 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 putResponse = await ddb.PutItemAsync(putRequest); - Debug.Assert(putResponse.HttpStatusCode == HttpStatusCode.OK); + var putRequest = new PutItemRequest + { + TableName = ddbTableName, + Item = item + }; - // 7. 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. - var key = new Dictionary - { - ["partition_key"] = new AttributeValue { S = partitionKeyValue }, - ["sort_key"] = new AttributeValue { N = sortKeyReadValue } - }; + var putResponse = await ddb.PutItemAsync(putRequest); + Debug.Assert(putResponse.HttpStatusCode == HttpStatusCode.OK); - var getRequest = new GetItemRequest - { - TableName = ddbTableName, - Key = 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. - ConsistentRead = true - }; + // 7. 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. + var key = new Dictionary + { + ["partition_key"] = new AttributeValue { S = partitionKeyValue }, + ["sort_key"] = new AttributeValue { N = sortKeyReadValue } + }; - var getResponse = await ddb.GetItemAsync(getRequest); - Debug.Assert(getResponse.HttpStatusCode == HttpStatusCode.OK); + var getRequest = new GetItemRequest + { + TableName = ddbTableName, + Key = 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. + ConsistentRead = true + }; - // 8. Verify we get the expected item back - if (getResponse.Item == null) - { - throw new Exception("No item found"); - } + var getResponse = await ddb.GetItemAsync(getRequest); + Debug.Assert(getResponse.HttpStatusCode == HttpStatusCode.OK); - bool success = MigrationUtils.VerifyReturnedItem(getResponse, partitionKeyValue, sortKeyReadValue); - if (success) - { - Console.WriteLine("MigrationStep2 completed successfully"); - } - return success; + // 8. Verify we get the expected item back + if (getResponse.Item == null) + { + throw new Exception("No item found"); } - catch (Exception e) + + bool success = MigrationUtils.VerifyReturnedItem(getResponse, partitionKeyValue, sortKeyReadValue); + if (success) { - Console.WriteLine($"Error in MigrationStep2: {e.Message}"); - throw; + Console.WriteLine("MigrationStep2 completed successfully"); } + return success; } } } diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2Test.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2Test.cs index 0d855ec2c..130e4101d 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2Test.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2Test.cs @@ -38,41 +38,36 @@ public async Task TestMigrationStep2() string partitionKey = Guid.NewGuid().ToString(); string[] sortKeys = { "0", "1", "2", "3" }; - try - { - // Given: Step 0 has succeeded - bool success = await MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[0]); - Assert.True(success, "MigrationStep0 should complete successfully"); + // Given: Step 0 has succeeded + bool success = await MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[0]); + Assert.True(success, "MigrationStep0 should complete successfully"); - // Given: Step 1 has succeeded - success = await MigrationStep1.MigrationStep1Example(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[1]); - Assert.True(success, "MigrationStep1 should complete successfully"); + // Given: Step 1 has succeeded + success = await MigrationStep1.MigrationStep1Example(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[1]); + Assert.True(success, "MigrationStep1 should complete successfully"); - // Successfully executes step 2 - success = await MigrationStep2.MigrationStep2Example(kmsKeyID, tableName, partitionKey, sortKeys[2], sortKeys[2]); - Assert.True(success, "MigrationStep2 should complete successfully"); + // Successfully executes step 2 + success = await MigrationStep2.MigrationStep2Example(kmsKeyID, tableName, partitionKey, sortKeys[2], sortKeys[2]); + Assert.True(success, "MigrationStep2 should complete successfully"); - // When: Execute Step 2 with sortReadValue=0, Then: Success (i.e. can read plaintext values from Step 0) - success = await MigrationStep2.MigrationStep2Example(kmsKeyID, tableName, partitionKey, sortKeys[2], sortKeys[0]); - Assert.True(success, "MigrationStep2 should be able to read items written by Step 0"); + // When: Execute Step 2 with sortReadValue=0, Then: Success (i.e. can read plaintext values from Step 0) + success = await MigrationStep2.MigrationStep2Example(kmsKeyID, tableName, partitionKey, sortKeys[2], sortKeys[0]); + Assert.True(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) - success = await MigrationStep2.MigrationStep2Example(kmsKeyID, tableName, partitionKey, sortKeys[2], sortKeys[1]); - Assert.True(success, "MigrationStep2 should be able to read items written by Step 1"); + // When: Execute Step 2 with sortReadValue=1, Then: Success (i.e. can read plaintext values from Step 1) + success = await MigrationStep2.MigrationStep2Example(kmsKeyID, tableName, partitionKey, sortKeys[2], sortKeys[1]); + Assert.True(success, "MigrationStep2 should be able to read items written by Step 1"); - // Given: Step 3 has succeeded - success = await MigrationStep3.MigrationStep3Example(kmsKeyID, tableName, partitionKey, sortKeys[3], sortKeys[3]); - Assert.True(success, "MigrationStep3 should complete successfully"); + // Given: Step 3 has succeeded + success = await MigrationStep3.MigrationStep3Example(kmsKeyID, tableName, partitionKey, sortKeys[3], sortKeys[3]); + Assert.True(success, "MigrationStep3 should complete successfully"); - // When: Execute Step 2 with sortReadValue=3, Then: Success (i.e. can read encrypted values from Step 3) - success = await MigrationStep2.MigrationStep2Example(kmsKeyID, tableName, partitionKey, sortKeys[2], sortKeys[3]); - Assert.True(success, "MigrationStep2 should be able to read items written by Step 3"); - } - finally - { - // Cleanup - await MigrationUtils.CleanupItems(tableName, partitionKey, sortKeys); - } + // When: Execute Step 2 with sortReadValue=3, Then: Success (i.e. can read encrypted values from Step 3) + success = await MigrationStep2.MigrationStep2Example(kmsKeyID, tableName, partitionKey, sortKeys[2], sortKeys[3]); + Assert.True(success, "MigrationStep2 should be able to read items written by Step 3"); + + // Cleanup + await MigrationUtils.CleanupItems(tableName, partitionKey, sortKeys); } } } diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3.cs index 1729d038c..2ba958e9f 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3.cs @@ -36,133 +36,125 @@ public class MigrationStep3 { public static async Task MigrationStep3Example(string kmsKeyId, string ddbTableName, string partitionKeyValue, string sortKeyWriteValue, string sortKeyReadValue) { - try - { - // 1. 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. - var matProv = new MaterialProviders(new MaterialProvidersConfig()); - var keyringInput = new CreateAwsKmsMrkMultiKeyringInput { Generator = kmsKeyId }; - var kmsKeyring = matProv.CreateAwsKmsMrkMultiKeyring(keyringInput); - - // 2. 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 - var attributeActionsOnEncrypt = new Dictionary - { - ["partition_key"] = CryptoAction.SIGN_ONLY, // Our partition attribute must be SIGN_ONLY - ["sort_key"] = CryptoAction.SIGN_ONLY, // Our sort attribute must be SIGN_ONLY - ["attribute1"] = CryptoAction.ENCRYPT_AND_SIGN, - ["attribute2"] = CryptoAction.SIGN_ONLY, - ["attribute3"] = CryptoAction.DO_NOTHING - }; + // 1. 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. + var matProv = new MaterialProviders(new MaterialProvidersConfig()); + var keyringInput = new CreateAwsKmsMrkMultiKeyringInput { Generator = kmsKeyId }; + var kmsKeyring = matProv.CreateAwsKmsMrkMultiKeyring(keyringInput); - // 3. Configure which attributes we expect to be excluded in the signature - // when reading items. - // For this example, we will explicitly list the attributes that are not signed. - var unsignedAttributes = new List { "attribute3" }; + // 2. 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 + var attributeActionsOnEncrypt = new Dictionary + { + ["partition_key"] = CryptoAction.SIGN_ONLY, // Our partition attribute must be SIGN_ONLY + ["sort_key"] = CryptoAction.SIGN_ONLY, // Our sort attribute must be SIGN_ONLY + ["attribute1"] = CryptoAction.ENCRYPT_AND_SIGN, + ["attribute2"] = CryptoAction.SIGN_ONLY, + ["attribute3"] = CryptoAction.DO_NOTHING + }; - // 4. Create the DynamoDb Encryption configuration for the table we will be writing to. - // This configuration uses PlaintextOverride.FORBID_PLAINTEXT_WRITE_FORBID_PLAINTEXT_READ - // 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 - // FORBID_PLAINTEXT_WRITE_FORBID_PLAINTEXT_READ, which is the desired - // behavior for a client interacting with a fully encrypted database. - var tableConfig = new DynamoDbTableEncryptionConfig - { - LogicalTableName = ddbTableName, - PartitionKeyName = "partition_key", - SortKeyName = "sort_key", - AttributeActionsOnEncrypt = attributeActionsOnEncrypt, - Keyring = kmsKeyring, - AllowedUnsignedAttributes = unsignedAttributes, - PlaintextOverride = PlaintextOverride.FORBID_PLAINTEXT_WRITE_FORBID_PLAINTEXT_READ - }; + // 3. Configure which attributes we expect to be excluded in the signature + // when reading items. + // For this example, we will explicitly list the attributes that are not signed. + var unsignedAttributes = new List { "attribute3" }; - var tableConfigs = new Dictionary - { - [ddbTableName] = tableConfig - }; + // 4. Create the DynamoDb Encryption configuration for the table we will be writing to. + // This configuration uses PlaintextOverride.FORBID_PLAINTEXT_WRITE_FORBID_PLAINTEXT_READ + // 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 + // FORBID_PLAINTEXT_WRITE_FORBID_PLAINTEXT_READ, which is the desired + // behavior for a client interacting with a fully encrypted database. + var tableConfig = new DynamoDbTableEncryptionConfig + { + LogicalTableName = ddbTableName, + PartitionKeyName = "partition_key", + SortKeyName = "sort_key", + AttributeActionsOnEncrypt = attributeActionsOnEncrypt, + Keyring = kmsKeyring, + AllowedUnsignedAttributes = unsignedAttributes, + PlaintextOverride = PlaintextOverride.FORBID_PLAINTEXT_WRITE_FORBID_PLAINTEXT_READ + }; - // 5. Create a new AWS SDK DynamoDb client using the TableEncryptionConfigs - var ddb = new Client.DynamoDbClient( - new DynamoDbTablesEncryptionConfig { TableEncryptionConfigs = tableConfigs }); + var tableConfigs = new Dictionary + { + [ddbTableName] = tableConfig + }; - // 6. Put an item into our table using the above client. - // This item will be encrypted due to our PlaintextOverride configuration. - string encryptedAndSignedValue = MigrationUtils.ENCRYPTED_AND_SIGNED_VALUE; - string signOnlyValue = MigrationUtils.SIGN_ONLY_VALUE; - string doNothingValue = MigrationUtils.DO_NOTHING_VALUE; - 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 } - }; + // 5. Create a new AWS SDK DynamoDb client using the TableEncryptionConfigs + var ddb = new Client.DynamoDbClient( + new DynamoDbTablesEncryptionConfig { TableEncryptionConfigs = tableConfigs }); - var putRequest = new PutItemRequest - { - TableName = ddbTableName, - Item = item - }; + // 6. Put an item into our table using the above client. + // This item will be encrypted due to our PlaintextOverride configuration. + string encryptedAndSignedValue = MigrationUtils.ENCRYPTED_AND_SIGNED_VALUE; + string signOnlyValue = MigrationUtils.SIGN_ONLY_VALUE; + string doNothingValue = MigrationUtils.DO_NOTHING_VALUE; + 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 putResponse = await ddb.PutItemAsync(putRequest); - Debug.Assert(putResponse.HttpStatusCode == HttpStatusCode.OK); + var putRequest = new PutItemRequest + { + TableName = ddbTableName, + Item = item + }; - // 7. 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. - var key = new Dictionary - { - ["partition_key"] = new AttributeValue { S = partitionKeyValue }, - ["sort_key"] = new AttributeValue { N = sortKeyReadValue } - }; + var putResponse = await ddb.PutItemAsync(putRequest); + Debug.Assert(putResponse.HttpStatusCode == HttpStatusCode.OK); - var getRequest = new GetItemRequest - { - TableName = ddbTableName, - Key = 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. - ConsistentRead = true - }; + // 7. 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. + var key = new Dictionary + { + ["partition_key"] = new AttributeValue { S = partitionKeyValue }, + ["sort_key"] = new AttributeValue { N = sortKeyReadValue } + }; - var getResponse = await ddb.GetItemAsync(getRequest); - Debug.Assert(getResponse.HttpStatusCode == HttpStatusCode.OK); + var getRequest = new GetItemRequest + { + TableName = ddbTableName, + Key = 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. + ConsistentRead = true + }; - // 8. Verify we get the expected item back - if (getResponse.Item == null) - { - throw new Exception("No item found"); - } + var getResponse = await ddb.GetItemAsync(getRequest); + Debug.Assert(getResponse.HttpStatusCode == HttpStatusCode.OK); - bool success = MigrationUtils.VerifyReturnedItem(getResponse, partitionKeyValue, sortKeyReadValue); - if (success) - { - Console.WriteLine("MigrationStep3 completed successfully"); - } - return success; + // 8. Verify we get the expected item back + if (getResponse.Item == null) + { + throw new Exception("No item found"); } - catch (Exception e) + + bool success = MigrationUtils.VerifyReturnedItem(getResponse, partitionKeyValue, sortKeyReadValue); + if (success) { - Console.WriteLine($"Error in MigrationStep3: {e.Message}"); - throw; + Console.WriteLine("MigrationStep3 completed successfully"); } + return success; } } } diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3Test.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3Test.cs index ce8b7ee37..333d62a46 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3Test.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3Test.cs @@ -39,44 +39,39 @@ public async Task TestMigrationStep3() string partitionKey = Guid.NewGuid().ToString(); string[] sortKeys = { "0", "1", "2", "3" }; - try - { - // Given: Step 0 has succeeded - bool success = await MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[0]); - Assert.True(success, "MigrationStep0 should complete successfully"); + // Given: Step 0 has succeeded + bool success = await MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[0]); + Assert.True(success, "MigrationStep0 should complete successfully"); - // Given: Step 1 has succeeded - success = await MigrationStep1.MigrationStep1Example(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[1]); - Assert.True(success, "MigrationStep1 should complete successfully"); + // Given: Step 1 has succeeded + success = await MigrationStep1.MigrationStep1Example(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[1]); + Assert.True(success, "MigrationStep1 should complete successfully"); - // Given: Step 2 has succeeded - success = await MigrationStep2.MigrationStep2Example(kmsKeyID, tableName, partitionKey, sortKeys[2], sortKeys[2]); - Assert.True(success, "MigrationStep2 should complete successfully"); - - // Successfully executes step 3 - success = await MigrationStep3.MigrationStep3Example(kmsKeyID, tableName, partitionKey, sortKeys[3], sortKeys[3]); - Assert.True(success, "MigrationStep3 should complete successfully"); - - // When: Execute Step 3 with sortReadValue=0, Then: should error out when reading plaintext items from Step 0 - var exception = await Assert.ThrowsAsync(() => - MigrationStep3.MigrationStep3Example(kmsKeyID, tableName, partitionKey, sortKeys[3], sortKeys[0])); - - Assert.Contains("encrypted item missing expected header and footer attributes", exception.Message.ToLower()); + // Given: Step 2 has succeeded + success = await MigrationStep2.MigrationStep2Example(kmsKeyID, tableName, partitionKey, sortKeys[2], sortKeys[2]); + Assert.True(success, "MigrationStep2 should complete successfully"); + + // Successfully executes step 3 + success = await MigrationStep3.MigrationStep3Example(kmsKeyID, tableName, partitionKey, sortKeys[3], sortKeys[3]); + Assert.True(success, "MigrationStep3 should complete successfully"); + + // When: Execute Step 3 with sortReadValue=0, Then: should error out when reading plaintext items from Step 0 + var exception = await Assert.ThrowsAsync(() => + MigrationStep3.MigrationStep3Example(kmsKeyID, tableName, partitionKey, sortKeys[3], sortKeys[0])); + + Assert.Contains("encrypted item missing expected header and footer attributes", exception.Message.ToLower()); - // When: Execute Step 3 with sortReadValue=1, Then: should error out when reading plaintext items from Step 1 - exception = await Assert.ThrowsAsync(() => - MigrationStep3.MigrationStep3Example(kmsKeyID, tableName, partitionKey, sortKeys[3], sortKeys[1])); - Assert.Contains("encrypted item missing expected header and footer attributes", exception.Message.ToLower()); + // When: Execute Step 3 with sortReadValue=1, Then: should error out when reading plaintext items from Step 1 + exception = await Assert.ThrowsAsync(() => + MigrationStep3.MigrationStep3Example(kmsKeyID, tableName, partitionKey, sortKeys[3], sortKeys[1])); + Assert.Contains("encrypted item missing expected header and footer attributes", exception.Message.ToLower()); - // When: Execute Step 3 with sortReadValue=2, Then: Success (i.e. can read encrypted values from Step 2) - success = await MigrationStep3.MigrationStep3Example(kmsKeyID, tableName, partitionKey, sortKeys[3], sortKeys[2]); - Assert.True(success, "MigrationStep3 should be able to read items written by Step 2"); - } - finally - { - // Cleanup - await MigrationUtils.CleanupItems(tableName, partitionKey, sortKeys); - } + // When: Execute Step 3 with sortReadValue=2, Then: Success (i.e. can read encrypted values from Step 2) + success = await MigrationStep3.MigrationStep3Example(kmsKeyID, tableName, partitionKey, sortKeys[3], sortKeys[2]); + Assert.True(success, "MigrationStep3 should be able to read items written by Step 2"); + + // Cleanup + await MigrationUtils.CleanupItems(tableName, partitionKey, sortKeys); } } } diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0.cs index 3727fa8d2..1b5a14ab4 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0.cs @@ -33,76 +33,68 @@ 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 = MigrationUtils.ENCRYPTED_AND_SIGNED_VALUE; - string signOnlyValue = MigrationUtils.SIGN_ONLY_VALUE; - string doNothingValue = MigrationUtils.DO_NOTHING_VALUE; - 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 } - }; + // 1. Create a standard DynamoDB client + var ddb = new AmazonDynamoDBClient(); - var putRequest = new PutItemRequest - { - TableName = ddbTableName, - Item = item - }; + // 2. Put an example item into DynamoDB table + // This item will be stored in plaintext. + string encryptedAndSignedValue = MigrationUtils.ENCRYPTED_AND_SIGNED_VALUE; + string signOnlyValue = MigrationUtils.SIGN_ONLY_VALUE; + string doNothingValue = MigrationUtils.DO_NOTHING_VALUE; + 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 putResponse = await ddb.PutItemAsync(putRequest); - Debug.Assert(putResponse.HttpStatusCode == HttpStatusCode.OK); + var putRequest = new PutItemRequest + { + TableName = ddbTableName, + Item = item + }; - // 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 putResponse = await ddb.PutItemAsync(putRequest); + Debug.Assert(putResponse.HttpStatusCode == HttpStatusCode.OK); - var getRequest = new GetItemRequest - { - TableName = ddbTableName, - Key = key - }; + // 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 getResponse = await ddb.GetItemAsync(getRequest); - Debug.Assert(getResponse.HttpStatusCode == HttpStatusCode.OK); + var getRequest = new GetItemRequest + { + TableName = ddbTableName, + Key = key + }; - // 4. Verify we get the expected item back - if (getResponse.Item == null) - { - throw new Exception("No item found"); - } + var getResponse = await ddb.GetItemAsync(getRequest); + Debug.Assert(getResponse.HttpStatusCode == HttpStatusCode.OK); - bool success = MigrationUtils.VerifyReturnedItem(getResponse, partitionKeyValue, sortKeyReadValue); - if (success) - { - Console.WriteLine("MigrationStep0 completed successfully"); - } - return success; + // 4. Verify we get the expected item back + if (getResponse.Item == null) + { + throw new Exception("No item found"); } - catch (Exception e) + + bool success = MigrationUtils.VerifyReturnedItem(getResponse, partitionKeyValue, sortKeyReadValue); + if (success) { - Console.WriteLine($"Error in MigrationStep0: {e.Message}"); - throw; + Console.WriteLine("MigrationStep0 completed successfully"); } + return success; } } diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0Test.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0Test.cs index 5382748e7..5147277b6 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0Test.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0Test.cs @@ -35,41 +35,35 @@ public async Task TestMigrationStep0() 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"); + // Successfully executes step 0 + bool success = await MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[0]); + Assert.True(success, "MigrationStep0 should complete successfully"); - // 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])); + // 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 mismatch", exception.Message); - - // Given: Step 3 has succeeded - 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 mismatch", exception.Message); - } - finally - { - // Cleanup - await MigrationUtils.CleanupItems(tableName, partitionKey, sortKeys); - } + Assert.Contains("attribute1 mismatch", exception.Message); + + // Given: Step 3 has succeeded + 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 mismatch", exception.Message); + + await MigrationUtils.CleanupItems(tableName, partitionKey, sortKeys); } } } From 5cb41558c453b8d44f937c42e296fa167d50b3e6 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Thu, 21 Aug 2025 16:05:39 -0700 Subject: [PATCH 04/25] auto commit --- .../migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs index 1b0e6ebd5..22ef058b9 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs @@ -37,8 +37,6 @@ public class MigrationStep1 { public static async Task MigrationStep1Example(string kmsKeyId, string ddbTableName, string partitionKeyValue, string sortKeyWriteValue, string sortKeyReadValue) { - try - { // 1. 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, @@ -178,12 +176,6 @@ public static async Task MigrationStep1Example(string kmsKeyId, string ddb Console.WriteLine("MigrationStep1 completed successfully"); } return success; - } - catch (Exception e) - { - Console.WriteLine($"Error in MigrationStep1: {e.Message}"); - throw; - } } } } From 896d90c5571b21d3601c93e35a765961ffe8b006 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Thu, 21 Aug 2025 16:07:13 -0700 Subject: [PATCH 05/25] auto commit --- .../awsdbe/MigrationStep1.cs | 252 +++++++++--------- 1 file changed, 126 insertions(+), 126 deletions(-) diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs index 22ef058b9..1bf7cb46c 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs @@ -37,145 +37,145 @@ public class MigrationStep1 { public static async Task MigrationStep1Example(string kmsKeyId, string ddbTableName, string partitionKeyValue, string sortKeyWriteValue, string sortKeyReadValue) { - // 1. 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. - var matProv = new MaterialProviders(new MaterialProvidersConfig()); - var keyringInput = new CreateAwsKmsMrkMultiKeyringInput { Generator = kmsKeyId }; - var kmsKeyring = matProv.CreateAwsKmsMrkMultiKeyring(keyringInput); + // 1. 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. + var matProv = new MaterialProviders(new MaterialProvidersConfig()); + var keyringInput = new CreateAwsKmsMrkMultiKeyringInput { Generator = kmsKeyId }; + var kmsKeyring = matProv.CreateAwsKmsMrkMultiKeyring(keyringInput); - // 2. 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 - var attributeActionsOnEncrypt = new Dictionary - { - ["partition_key"] = CryptoAction.SIGN_ONLY, // Our partition attribute must be SIGN_ONLY - ["sort_key"] = CryptoAction.SIGN_ONLY, // Our sort attribute must be SIGN_ONLY - ["attribute1"] = CryptoAction.ENCRYPT_AND_SIGN, - ["attribute2"] = CryptoAction.SIGN_ONLY, - ["attribute3"] = CryptoAction.DO_NOTHING - }; + // 2. 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 + var attributeActionsOnEncrypt = new Dictionary + { + ["partition_key"] = CryptoAction.SIGN_ONLY, // Our partition attribute must be SIGN_ONLY + ["sort_key"] = CryptoAction.SIGN_ONLY, // Our sort attribute must be SIGN_ONLY + ["attribute1"] = CryptoAction.ENCRYPT_AND_SIGN, + ["attribute2"] = CryptoAction.SIGN_ONLY, + ["attribute3"] = CryptoAction.DO_NOTHING + }; - // 3. 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. - var unsignedAttributes = new List { "attribute3" }; + // 3. 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. + var unsignedAttributes = new List { "attribute3" }; - // 4. Create the DynamoDb Encryption configuration for the table we will be writing to. - // This configuration uses PlaintextOverride.FORCE_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ - // 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. - var tableConfig = new DynamoDbTableEncryptionConfig - { - LogicalTableName = ddbTableName, - PartitionKeyName = "partition_key", - SortKeyName = "sort_key", - AttributeActionsOnEncrypt = attributeActionsOnEncrypt, - Keyring = kmsKeyring, - AllowedUnsignedAttributes = unsignedAttributes, - PlaintextOverride = PlaintextOverride.FORCE_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ - }; + // 4. Create the DynamoDb Encryption configuration for the table we will be writing to. + // This configuration uses PlaintextOverride.FORCE_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ + // 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. + var tableConfig = new DynamoDbTableEncryptionConfig + { + LogicalTableName = ddbTableName, + PartitionKeyName = "partition_key", + SortKeyName = "sort_key", + AttributeActionsOnEncrypt = attributeActionsOnEncrypt, + Keyring = kmsKeyring, + AllowedUnsignedAttributes = unsignedAttributes, + PlaintextOverride = PlaintextOverride.FORCE_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ + }; - var tableConfigs = new Dictionary - { - [ddbTableName] = tableConfig - }; + var tableConfigs = new Dictionary + { + [ddbTableName] = tableConfig + }; - // 5. Create a new AWS SDK DynamoDb client using the TableEncryptionConfigs - var ddb = new Client.DynamoDbClient( - new DynamoDbTablesEncryptionConfig { TableEncryptionConfigs = tableConfigs }); + // 5. Create a new AWS SDK DynamoDb client using the TableEncryptionConfigs + var ddb = new Client.DynamoDbClient( + new DynamoDbTablesEncryptionConfig { TableEncryptionConfigs = tableConfigs }); - // 6. Put an item into our table using the above client. - // This item will be stored in plaintext due to our PlaintextOverride configuration. - string encryptedAndSignedValue = MigrationUtils.ENCRYPTED_AND_SIGNED_VALUE; - string signOnlyValue = MigrationUtils.SIGN_ONLY_VALUE; - string doNothingValue = MigrationUtils.DO_NOTHING_VALUE; - 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 } - }; + // 6. Put an item into our table using the above client. + // This item will be stored in plaintext due to our PlaintextOverride configuration. + string encryptedAndSignedValue = MigrationUtils.ENCRYPTED_AND_SIGNED_VALUE; + string signOnlyValue = MigrationUtils.SIGN_ONLY_VALUE; + string doNothingValue = MigrationUtils.DO_NOTHING_VALUE; + 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 putRequest = new PutItemRequest + { + TableName = ddbTableName, + Item = item + }; - var putResponse = await ddb.PutItemAsync(putRequest); - Debug.Assert(putResponse.HttpStatusCode == HttpStatusCode.OK); + var putResponse = await ddb.PutItemAsync(putRequest); + Debug.Assert(putResponse.HttpStatusCode == HttpStatusCode.OK); - // 7. 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. - var key = new Dictionary - { - ["partition_key"] = new AttributeValue { S = partitionKeyValue }, - ["sort_key"] = new AttributeValue { N = sortKeyReadValue } - }; + // 7. 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. + var key = new Dictionary + { + ["partition_key"] = new AttributeValue { S = partitionKeyValue }, + ["sort_key"] = new AttributeValue { N = sortKeyReadValue } + }; - var getRequest = new GetItemRequest - { - TableName = ddbTableName, - Key = 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. - ConsistentRead = true - }; + var getRequest = new GetItemRequest + { + TableName = ddbTableName, + Key = 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. + ConsistentRead = true + }; - var getResponse = await ddb.GetItemAsync(getRequest); - Debug.Assert(getResponse.HttpStatusCode == HttpStatusCode.OK); + var getResponse = await ddb.GetItemAsync(getRequest); + Debug.Assert(getResponse.HttpStatusCode == HttpStatusCode.OK); - // 8. Verify we get the expected item back - if (getResponse.Item == null) - { - throw new Exception("No item found"); - } + // 8. Verify we get the expected item back + if (getResponse.Item == null) + { + throw new Exception("No item found"); + } - bool success = MigrationUtils.VerifyReturnedItem(getResponse, partitionKeyValue, sortKeyReadValue); - if (success) - { - Console.WriteLine("MigrationStep1 completed successfully"); - } - return success; + bool success = MigrationUtils.VerifyReturnedItem(getResponse, partitionKeyValue, sortKeyReadValue); + if (success) + { + Console.WriteLine("MigrationStep1 completed successfully"); + } + return success; } } } From f0f7c549860f9ecff8cc4b335c1c70e9be382c46 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Thu, 21 Aug 2025 16:29:38 -0700 Subject: [PATCH 06/25] auto commit --- .../awsdbe/MigrationStep1.cs | 83 ++--------------- .../awsdbe/MigrationStep2.cs | 66 +++----------- .../awsdbe/MigrationStep3.cs | 70 +++------------ .../PlaintextToAWSDBE/awsdbe/common.cs | 88 +++++++++++++++++++ 4 files changed, 119 insertions(+), 188 deletions(-) create mode 100644 Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/common.cs diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs index 1bf7cb46c..06200e258 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs @@ -37,86 +37,13 @@ public class MigrationStep1 { public static async Task MigrationStep1Example(string kmsKeyId, string ddbTableName, string partitionKeyValue, string sortKeyWriteValue, string sortKeyReadValue) { - // 1. 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. - var matProv = new MaterialProviders(new MaterialProvidersConfig()); - var keyringInput = new CreateAwsKmsMrkMultiKeyringInput { Generator = kmsKeyId }; - var kmsKeyring = matProv.CreateAwsKmsMrkMultiKeyring(keyringInput); + var tableConfigs = Common.CreateTableConfigs(kmsKeyId, ddbTableName, PlaintextOverride.FORCE_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ); - // 2. 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 - var attributeActionsOnEncrypt = new Dictionary - { - ["partition_key"] = CryptoAction.SIGN_ONLY, // Our partition attribute must be SIGN_ONLY - ["sort_key"] = CryptoAction.SIGN_ONLY, // Our sort attribute must be SIGN_ONLY - ["attribute1"] = CryptoAction.ENCRYPT_AND_SIGN, - ["attribute2"] = CryptoAction.SIGN_ONLY, - ["attribute3"] = CryptoAction.DO_NOTHING - }; - - // 3. 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. - var unsignedAttributes = new List { "attribute3" }; - - // 4. Create the DynamoDb Encryption configuration for the table we will be writing to. - // This configuration uses PlaintextOverride.FORCE_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ - // 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. - var tableConfig = new DynamoDbTableEncryptionConfig - { - LogicalTableName = ddbTableName, - PartitionKeyName = "partition_key", - SortKeyName = "sort_key", - AttributeActionsOnEncrypt = attributeActionsOnEncrypt, - Keyring = kmsKeyring, - AllowedUnsignedAttributes = unsignedAttributes, - PlaintextOverride = PlaintextOverride.FORCE_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ - }; - - var tableConfigs = new Dictionary - { - [ddbTableName] = tableConfig - }; - - // 5. Create a new AWS SDK DynamoDb client using the TableEncryptionConfigs + // 1. Create a new AWS SDK DynamoDb client using the TableEncryptionConfigs var ddb = new Client.DynamoDbClient( new DynamoDbTablesEncryptionConfig { TableEncryptionConfigs = tableConfigs }); - // 6. Put an item into our table using the above client. + // 2. Put an item into our table using the above client. // This item will be stored in plaintext due to our PlaintextOverride configuration. string encryptedAndSignedValue = MigrationUtils.ENCRYPTED_AND_SIGNED_VALUE; string signOnlyValue = MigrationUtils.SIGN_ONLY_VALUE; @@ -139,7 +66,7 @@ public static async Task MigrationStep1Example(string kmsKeyId, string ddb var putResponse = await ddb.PutItemAsync(putRequest); Debug.Assert(putResponse.HttpStatusCode == HttpStatusCode.OK); - // 7. Get an item back from the table using the same client. + // 3. 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 @@ -164,7 +91,7 @@ public static async Task MigrationStep1Example(string kmsKeyId, string ddb var getResponse = await ddb.GetItemAsync(getRequest); Debug.Assert(getResponse.HttpStatusCode == HttpStatusCode.OK); - // 8. Verify we get the expected item back + // 4. Verify we get the expected item back if (getResponse.Item == null) { throw new Exception("No item found"); diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2.cs index 81ec855e6..5358d8220 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2.cs @@ -39,62 +39,20 @@ public class MigrationStep2 { public static async Task MigrationStep2Example(string kmsKeyId, string ddbTableName, string partitionKeyValue, string sortKeyWriteValue, string sortKeyReadValue) { - // 1. 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. - var matProv = new MaterialProviders(new MaterialProvidersConfig()); - var keyringInput = new CreateAwsKmsMrkMultiKeyringInput { Generator = kmsKeyId }; - var kmsKeyring = matProv.CreateAwsKmsMrkMultiKeyring(keyringInput); + // 1. Create table configurations + // In this of migration we will use PlaintextOverride.FORBID_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ + // 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. + var tableConfigs = Common.CreateTableConfigs(kmsKeyId, ddbTableName, PlaintextOverride.FORBID_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ); - // 2. 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 - var attributeActionsOnEncrypt = new Dictionary - { - ["partition_key"] = CryptoAction.SIGN_ONLY, // Our partition attribute must be SIGN_ONLY - ["sort_key"] = CryptoAction.SIGN_ONLY, // Our sort attribute must be SIGN_ONLY - ["attribute1"] = CryptoAction.ENCRYPT_AND_SIGN, - ["attribute2"] = CryptoAction.SIGN_ONLY, - ["attribute3"] = CryptoAction.DO_NOTHING - }; - - // 3. Configure which attributes we expect to be excluded in the signature - // when reading items. - // For this example, we will explicitly list the attributes that are not signed. - var unsignedAttributes = new List { "attribute3" }; - - // 4. Create the DynamoDb Encryption configuration for the table we will be writing to. - // This configuration uses PlaintextOverride.FORBID_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ - // 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. - var tableConfig = new DynamoDbTableEncryptionConfig - { - LogicalTableName = ddbTableName, - PartitionKeyName = "partition_key", - SortKeyName = "sort_key", - AttributeActionsOnEncrypt = attributeActionsOnEncrypt, - Keyring = kmsKeyring, - AllowedUnsignedAttributes = unsignedAttributes, - PlaintextOverride = PlaintextOverride.FORBID_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ - }; - - var tableConfigs = new Dictionary - { - [ddbTableName] = tableConfig - }; - - // 5. Create a new AWS SDK DynamoDb client using the TableEncryptionConfigs + // 2. Create a new AWS SDK DynamoDb client using the TableEncryptionConfigs var ddb = new Client.DynamoDbClient( new DynamoDbTablesEncryptionConfig { TableEncryptionConfigs = tableConfigs }); - // 6. Put an item into our table using the above client. + // 3. Put an item into our table using the above client. // This item will be encrypted due to our PlaintextOverride configuration. string encryptedAndSignedValue = MigrationUtils.ENCRYPTED_AND_SIGNED_VALUE; string signOnlyValue = MigrationUtils.SIGN_ONLY_VALUE; @@ -117,7 +75,7 @@ public static async Task MigrationStep2Example(string kmsKeyId, string ddb var putResponse = await ddb.PutItemAsync(putRequest); Debug.Assert(putResponse.HttpStatusCode == HttpStatusCode.OK); - // 7. Get an item back from the table using the same client. + // 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 @@ -142,7 +100,7 @@ public static async Task MigrationStep2Example(string kmsKeyId, string ddb var getResponse = await ddb.GetItemAsync(getRequest); Debug.Assert(getResponse.HttpStatusCode == HttpStatusCode.OK); - // 8. Verify we get the expected item back + // 5. Verify we get the expected item back if (getResponse.Item == null) { throw new Exception("No item found"); diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3.cs index 2ba958e9f..73bc0316e 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3.cs @@ -36,65 +36,23 @@ public class MigrationStep3 { public static async Task MigrationStep3Example(string kmsKeyId, string ddbTableName, string partitionKeyValue, string sortKeyWriteValue, string sortKeyReadValue) { - // 1. 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. - var matProv = new MaterialProviders(new MaterialProvidersConfig()); - var keyringInput = new CreateAwsKmsMrkMultiKeyringInput { Generator = kmsKeyId }; - var kmsKeyring = matProv.CreateAwsKmsMrkMultiKeyring(keyringInput); - - // 2. 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 - var attributeActionsOnEncrypt = new Dictionary - { - ["partition_key"] = CryptoAction.SIGN_ONLY, // Our partition attribute must be SIGN_ONLY - ["sort_key"] = CryptoAction.SIGN_ONLY, // Our sort attribute must be SIGN_ONLY - ["attribute1"] = CryptoAction.ENCRYPT_AND_SIGN, - ["attribute2"] = CryptoAction.SIGN_ONLY, - ["attribute3"] = CryptoAction.DO_NOTHING - }; - - // 3. Configure which attributes we expect to be excluded in the signature - // when reading items. - // For this example, we will explicitly list the attributes that are not signed. - var unsignedAttributes = new List { "attribute3" }; - - // 4. Create the DynamoDb Encryption configuration for the table we will be writing to. - // This configuration uses PlaintextOverride.FORBID_PLAINTEXT_WRITE_FORBID_PLAINTEXT_READ - // which means: - // - Write: Items are forbidden to be written as plaintext. + // 1. Create table configurations + // In this of migration we will use PlaintextOverride.FORBID_PLAINTEXT_WRITE_FORBID_PLAINTEXT_READ + // 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 - // FORBID_PLAINTEXT_WRITE_FORBID_PLAINTEXT_READ, which is the desired - // behavior for a client interacting with a fully encrypted database. - var tableConfig = new DynamoDbTableEncryptionConfig - { - LogicalTableName = ddbTableName, - PartitionKeyName = "partition_key", - SortKeyName = "sort_key", - AttributeActionsOnEncrypt = attributeActionsOnEncrypt, - Keyring = kmsKeyring, - AllowedUnsignedAttributes = unsignedAttributes, - PlaintextOverride = PlaintextOverride.FORBID_PLAINTEXT_WRITE_FORBID_PLAINTEXT_READ - }; - - var tableConfigs = new Dictionary - { - [ddbTableName] = tableConfig - }; + // - 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 + // FORBID_PLAINTEXT_WRITE_FORBID_PLAINTEXT_READ, which is the desired + // behavior for a client interacting with a fully encrypted database. + var tableConfigs = Common.CreateTableConfigs(kmsKeyId, ddbTableName, PlaintextOverride.FORBID_PLAINTEXT_WRITE_FORBID_PLAINTEXT_READ); - // 5. Create a new AWS SDK DynamoDb client using the TableEncryptionConfigs + // 2. Create a new AWS SDK DynamoDb client using the TableEncryptionConfigs var ddb = new Client.DynamoDbClient( new DynamoDbTablesEncryptionConfig { TableEncryptionConfigs = tableConfigs }); - // 6. Put an item into our table using the above client. + // 3. Put an item into our table using the above client. // This item will be encrypted due to our PlaintextOverride configuration. string encryptedAndSignedValue = MigrationUtils.ENCRYPTED_AND_SIGNED_VALUE; string signOnlyValue = MigrationUtils.SIGN_ONLY_VALUE; @@ -117,7 +75,7 @@ public static async Task MigrationStep3Example(string kmsKeyId, string ddb var putResponse = await ddb.PutItemAsync(putRequest); Debug.Assert(putResponse.HttpStatusCode == HttpStatusCode.OK); - // 7. Get an item back from the table using the same client. + // 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. @@ -143,7 +101,7 @@ public static async Task MigrationStep3Example(string kmsKeyId, string ddb var getResponse = await ddb.GetItemAsync(getRequest); Debug.Assert(getResponse.HttpStatusCode == HttpStatusCode.OK); - // 8. Verify we get the expected item back + // Verify we get the expected item back if (getResponse.Item == null) { throw new Exception("No item found"); diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/common.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/common.cs new file mode 100644 index 000000000..ec7e22258 --- /dev/null +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/common.cs @@ -0,0 +1,88 @@ +using System.Collections.Generic; +using AWS.Cryptography.DbEncryptionSDK.DynamoDb; +using AWS.Cryptography.DbEncryptionSDK.StructuredEncryption; +using AWS.Cryptography.MaterialProviders; + +namespace Examples.migration.PlaintextToAWSDBE +{ + public static class Common + { + public static Dictionary CreateTableConfigs(string kmsKeyId, string ddbTableName, PlaintextOverride PlaintextOverride) + { + // 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. + var matProv = new MaterialProviders(new MaterialProvidersConfig()); + var keyringInput = new CreateAwsKmsMrkMultiKeyringInput { Generator = kmsKeyId }; + var kmsKeyring = matProv.CreateAwsKmsMrkMultiKeyring(keyringInput); + + // 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 + var attributeActionsOnEncrypt = new Dictionary + { + ["partition_key"] = CryptoAction.SIGN_ONLY, + ["sort_key"] = CryptoAction.SIGN_ONLY, + ["attribute1"] = CryptoAction.ENCRYPT_AND_SIGN, + ["attribute2"] = CryptoAction.SIGN_ONLY, + ["attribute3"] = CryptoAction.DO_NOTHING + }; + + // 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. + var unsignedAttributes = new List { "attribute3" }; + + // Create the DynamoDb Encryption configuration for the table we will be writing to. + // This configuration uses PlaintextOverride.FORCE_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ + // 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. + var tableConfig = new DynamoDbTableEncryptionConfig + { + LogicalTableName = ddbTableName, + PartitionKeyName = "partition_key", + SortKeyName = "sort_key", + AttributeActionsOnEncrypt = attributeActionsOnEncrypt, + Keyring = kmsKeyring, + AllowedUnsignedAttributes = unsignedAttributes, + PlaintextOverride = PlaintextOverride + }; + + return new Dictionary + { + [ddbTableName] = tableConfig + }; + } + } +} From 06b23361d7678fc7e6114d7b569d22ee8ba62609 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Thu, 21 Aug 2025 16:30:30 -0700 Subject: [PATCH 07/25] auto commit --- .../net/src/migration/PlaintextToAWSDBE/awsdbe/common.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/common.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/common.cs index ec7e22258..daa2eb1ba 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/common.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/common.cs @@ -62,12 +62,6 @@ public static Dictionary CreateTableConfi var unsignedAttributes = new List { "attribute3" }; // Create the DynamoDb Encryption configuration for the table we will be writing to. - // This configuration uses PlaintextOverride.FORCE_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ - // 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. var tableConfig = new DynamoDbTableEncryptionConfig { LogicalTableName = ddbTableName, From 252d053c78add4f1e4318e2a17db792c60ba05c5 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Thu, 21 Aug 2025 16:31:09 -0700 Subject: [PATCH 08/25] auto commit --- .../migration/PlaintextToAWSDBE/awsdbe/{common.cs => Common.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/{common.cs => Common.cs} (100%) diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/common.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/Common.cs similarity index 100% rename from Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/common.cs rename to Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/Common.cs From 75ad2afbf3506a4419b429a2f9438bdec356dbaa Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Thu, 21 Aug 2025 16:42:55 -0700 Subject: [PATCH 09/25] auto commit --- Examples/runtimes/net/src/TestUtils.cs | 18 +++++++ .../PlaintextToAWSDBE/MigrationUtils.cs | 53 +++---------------- .../awsdbe/MigrationStep1Test.cs | 5 +- .../awsdbe/MigrationStep2Test.cs | 5 +- .../awsdbe/MigrationStep3Test.cs | 5 +- .../plaintext/MigrationStep0Test.cs | 8 ++- 6 files changed, 44 insertions(+), 50 deletions(-) diff --git a/Examples/runtimes/net/src/TestUtils.cs b/Examples/runtimes/net/src/TestUtils.cs index 746c50560..2ad5c4a26 100644 --- a/Examples/runtimes/net/src/TestUtils.cs +++ b/Examples/runtimes/net/src/TestUtils.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Collections.Generic; using Amazon.DynamoDBv2.Model; public class TestUtils @@ -84,4 +85,21 @@ public static void PrintAttributeValue(AttributeValue value) if (value.IsBOOLSet) Console.Write($"BOOL {value.BOOL}\n"); Console.Write("UNKNOWN\n"); } + + // Helper method to clean up test items + public static async System.Threading.Tasks.Task CleanupItems(string tableName, string partitionKey, string sortKey) + { + var ddb = new Amazon.DynamoDBv2.AmazonDynamoDBClient(); + 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); + } } \ No newline at end of file diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/MigrationUtils.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/MigrationUtils.cs index 1358842ac..fa1b7a5e7 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/MigrationUtils.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/MigrationUtils.cs @@ -16,14 +16,8 @@ public class MigrationUtils public static readonly string DO_NOTHING_VALUE = "this will never be encrypted nor signed"; // Verify that a returned item matches the expected values - public static bool VerifyReturnedItem(GetItemResponse response, string partitionKeyValue, string sortKeyValue, - string encryptedAndSignedValue = null, string signOnlyValue = null, string doNothingValue = null) - { - // Use default values if not provided - encryptedAndSignedValue = encryptedAndSignedValue ?? ENCRYPTED_AND_SIGNED_VALUE; - signOnlyValue = signOnlyValue ?? SIGN_ONLY_VALUE; - doNothingValue = doNothingValue ?? DO_NOTHING_VALUE; - + public static bool VerifyReturnedItem(GetItemResponse response, string partitionKeyValue, string sortKeyValue) + { var item = response.Item; // Verify partition key @@ -39,55 +33,24 @@ public static bool VerifyReturnedItem(GetItemResponse response, string partition } // Verify attribute1 (will be encrypted and signed in future steps) - if (!item.ContainsKey("attribute1") || item["attribute1"].S != encryptedAndSignedValue) + if (!item.ContainsKey("attribute1") || item["attribute1"].S != ENCRYPTED_AND_SIGNED_VALUE) { - throw new Exception($"attribute1 mismatch: expected {encryptedAndSignedValue}, got {(item.ContainsKey("attribute1") ? item["attribute1"].S : "null")}"); + throw new Exception($"attribute1 mismatch: expected {ENCRYPTED_AND_SIGNED_VALUE}, 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) + if (!item.ContainsKey("attribute2") || item["attribute2"].S != SIGN_ONLY_VALUE) { - throw new Exception($"attribute2 mismatch: expected {signOnlyValue}, got {(item.ContainsKey("attribute2") ? item["attribute2"].S : "null")}"); + throw new Exception($"attribute2 mismatch: expected {SIGN_ONLY_VALUE}, 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) + if (!item.ContainsKey("attribute3") || item["attribute3"].S != DO_NOTHING_VALUE) { - throw new Exception($"attribute3 mismatch: expected {doNothingValue}, got {(item.ContainsKey("attribute3") ? item["attribute3"].S : "null")}"); + throw new Exception($"attribute3 mismatch: expected {DO_NOTHING_VALUE}, 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/MigrationStep1Test.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1Test.cs index 1e2f76a5e..605f89f61 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1Test.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1Test.cs @@ -67,7 +67,10 @@ public async Task TestMigrationStep1() Assert.True(success, "MigrationStep1 should be able to read items written by Step 3"); // Cleanup - await MigrationUtils.CleanupItems(tableName, partitionKey, sortKeys); + foreach (var sortKey in sortKeys) + { + await TestUtils.CleanupItems(tableName, partitionKey, sortKey); + } } } } diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2Test.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2Test.cs index 130e4101d..73865011b 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2Test.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2Test.cs @@ -67,7 +67,10 @@ public async Task TestMigrationStep2() Assert.True(success, "MigrationStep2 should be able to read items written by Step 3"); // Cleanup - await MigrationUtils.CleanupItems(tableName, partitionKey, sortKeys); + foreach (var sortKey in sortKeys) + { + await TestUtils.CleanupItems(tableName, partitionKey, sortKey); + } } } } diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3Test.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3Test.cs index 333d62a46..2bf81c333 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3Test.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3Test.cs @@ -71,7 +71,10 @@ public async Task TestMigrationStep3() Assert.True(success, "MigrationStep3 should be able to read items written by Step 2"); // Cleanup - await MigrationUtils.CleanupItems(tableName, partitionKey, sortKeys); + foreach (var sortKey in sortKeys) + { + await TestUtils.CleanupItems(tableName, partitionKey, sortKey); + } } } } diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0Test.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0Test.cs index 5147277b6..6675ae832 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0Test.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0Test.cs @@ -62,8 +62,12 @@ public async Task TestMigrationStep0() exception = await Assert.ThrowsAsync(() => MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[3])); Assert.Contains("attribute1 mismatch", exception.Message); - - await MigrationUtils.CleanupItems(tableName, partitionKey, sortKeys); + + // Cleanup + foreach (var sortKey in sortKeys) + { + await TestUtils.CleanupItems(tableName, partitionKey, sortKey); + } } } } From 25df5b37791d0f002c6cdedb40e529e44635c4ec Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Thu, 21 Aug 2025 16:48:21 -0700 Subject: [PATCH 10/25] auto commit --- .../net/src/migration/PlaintextToAWSDBE/MigrationUtils.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/MigrationUtils.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/MigrationUtils.cs index fa1b7a5e7..5f4eb7508 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/MigrationUtils.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/MigrationUtils.cs @@ -20,31 +20,26 @@ public static bool VerifyReturnedItem(GetItemResponse response, string partition { 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 != ENCRYPTED_AND_SIGNED_VALUE) { throw new Exception($"attribute1 mismatch: expected {ENCRYPTED_AND_SIGNED_VALUE}, 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 != SIGN_ONLY_VALUE) { throw new Exception($"attribute2 mismatch: expected {SIGN_ONLY_VALUE}, 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 != DO_NOTHING_VALUE) { throw new Exception($"attribute3 mismatch: expected {DO_NOTHING_VALUE}, got {(item.ContainsKey("attribute3") ? item["attribute3"].S : "null")}"); From 315c509ab10b339ce636047abaa9ed2c4460270e Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Thu, 21 Aug 2025 16:52:19 -0700 Subject: [PATCH 11/25] auto commit --- .../src/migration/PlaintextToAWSDBE/awsdbe/Common.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/Common.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/Common.cs index daa2eb1ba..4bcbfec0c 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/Common.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/Common.cs @@ -23,10 +23,12 @@ public static Dictionary CreateTableConfi // - 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 + var partitionKeyName = "partition_key"; + var sortKeyName = "sort_key"; var attributeActionsOnEncrypt = new Dictionary { - ["partition_key"] = CryptoAction.SIGN_ONLY, - ["sort_key"] = CryptoAction.SIGN_ONLY, + [partitionKeyName] = CryptoAction.SIGN_ONLY, + [sortKeyName] = CryptoAction.SIGN_ONLY, ["attribute1"] = CryptoAction.ENCRYPT_AND_SIGN, ["attribute2"] = CryptoAction.SIGN_ONLY, ["attribute3"] = CryptoAction.DO_NOTHING @@ -65,8 +67,8 @@ public static Dictionary CreateTableConfi var tableConfig = new DynamoDbTableEncryptionConfig { LogicalTableName = ddbTableName, - PartitionKeyName = "partition_key", - SortKeyName = "sort_key", + PartitionKeyName = partitionKeyName, + SortKeyName = sortKeyName, AttributeActionsOnEncrypt = attributeActionsOnEncrypt, Keyring = kmsKeyring, AllowedUnsignedAttributes = unsignedAttributes, From 373bc932083953cd4b7917ca2dec2c1f6917ce93 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Thu, 21 Aug 2025 16:53:55 -0700 Subject: [PATCH 12/25] auto commit --- .../src/migration/PlaintextToAWSDBE/awsdbe/Common.cs | 4 ++-- .../PlaintextToAWSDBE/awsdbe/MigrationStep1.cs | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/Common.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/Common.cs index 4bcbfec0c..a8fc3a122 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/Common.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/Common.cs @@ -23,8 +23,8 @@ public static Dictionary CreateTableConfi // - 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 - var partitionKeyName = "partition_key"; - var sortKeyName = "sort_key"; + string partitionKeyName = "partition_key"; + string sortKeyName = "sort_key"; var attributeActionsOnEncrypt = new Dictionary { [partitionKeyName] = CryptoAction.SIGN_ONLY, diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs index 06200e258..0ae82ec5e 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs @@ -45,13 +45,15 @@ public static async Task MigrationStep1Example(string kmsKeyId, string ddb // 2. Put an item into our table using the above client. // This item will be stored in plaintext due to our PlaintextOverride configuration. + string partitionKeyName = "partition_key"; + string sortKeyName = "sort_key"; string encryptedAndSignedValue = MigrationUtils.ENCRYPTED_AND_SIGNED_VALUE; string signOnlyValue = MigrationUtils.SIGN_ONLY_VALUE; string doNothingValue = MigrationUtils.DO_NOTHING_VALUE; var item = new Dictionary { - ["partition_key"] = new AttributeValue { S = partitionKeyValue }, - ["sort_key"] = new AttributeValue { N = sortKeyWriteValue }, + [partitionKeyName] = new AttributeValue { S = partitionKeyValue }, + [sortKeyName] = new AttributeValue { N = sortKeyWriteValue }, ["attribute1"] = new AttributeValue { S = encryptedAndSignedValue }, ["attribute2"] = new AttributeValue { S = signOnlyValue }, ["attribute3"] = new AttributeValue { S = doNothingValue } @@ -74,8 +76,8 @@ public static async Task MigrationStep1Example(string kmsKeyId, string ddb // and surfaced as a plaintext item. var key = new Dictionary { - ["partition_key"] = new AttributeValue { S = partitionKeyValue }, - ["sort_key"] = new AttributeValue { N = sortKeyReadValue } + [partitionKeyName] = new AttributeValue { S = partitionKeyValue }, + [sortKeyName] = new AttributeValue { N = sortKeyReadValue } }; var getRequest = new GetItemRequest From 32fcb49b60eefc26e4efd01c7900e241c8cda486 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Thu, 21 Aug 2025 16:54:34 -0700 Subject: [PATCH 13/25] auto commit --- .../PlaintextToAWSDBE/awsdbe/MigrationStep2.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2.cs index 5358d8220..cae8ee5bf 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2.cs @@ -54,13 +54,15 @@ public static async Task MigrationStep2Example(string kmsKeyId, string ddb // 3. Put an item into our table using the above client. // This item will be encrypted due to our PlaintextOverride configuration. + string partitionKeyName = "partition_key"; + string sortKeyName = "sort_key"; string encryptedAndSignedValue = MigrationUtils.ENCRYPTED_AND_SIGNED_VALUE; string signOnlyValue = MigrationUtils.SIGN_ONLY_VALUE; string doNothingValue = MigrationUtils.DO_NOTHING_VALUE; var item = new Dictionary { - ["partition_key"] = new AttributeValue { S = partitionKeyValue }, - ["sort_key"] = new AttributeValue { N = sortKeyWriteValue }, + [partitionKeyName] = new AttributeValue { S = partitionKeyValue }, + [sortKeyName] = new AttributeValue { N = sortKeyWriteValue }, ["attribute1"] = new AttributeValue { S = encryptedAndSignedValue }, ["attribute2"] = new AttributeValue { S = signOnlyValue }, ["attribute3"] = new AttributeValue { S = doNothingValue } @@ -83,8 +85,8 @@ public static async Task MigrationStep2Example(string kmsKeyId, string ddb // and surfaced as a plaintext item. var key = new Dictionary { - ["partition_key"] = new AttributeValue { S = partitionKeyValue }, - ["sort_key"] = new AttributeValue { N = sortKeyReadValue } + [partitionKeyName] = new AttributeValue { S = partitionKeyValue }, + [sortKeyName] = new AttributeValue { N = sortKeyReadValue } }; var getRequest = new GetItemRequest From 37a60c5d2e611018729bb44dd756d780a9f49c85 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Thu, 21 Aug 2025 16:59:44 -0700 Subject: [PATCH 14/25] auto commit --- .../src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2.cs | 2 +- .../src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3.cs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2.cs index cae8ee5bf..ffc345dde 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2.cs @@ -43,7 +43,7 @@ public static async Task MigrationStep2Example(string kmsKeyId, string ddb // In this of migration we will use PlaintextOverride.FORBID_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ // which means: // - Write: Items are forbidden to be written as plaintext. - // Items will be written as encrypted items. + // 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. var tableConfigs = Common.CreateTableConfigs(kmsKeyId, ddbTableName, PlaintextOverride.FORBID_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ); diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3.cs index 73bc0316e..95cbd947f 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3.cs @@ -40,7 +40,7 @@ public static async Task MigrationStep3Example(string kmsKeyId, string ddb // In this of migration we will use PlaintextOverride.FORBID_PLAINTEXT_WRITE_FORBID_PLAINTEXT_READ // which means: // - Write: Items are forbidden to be written as plaintext. - // Items will be written as encrypted items. + // 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 @@ -54,6 +54,8 @@ public static async Task MigrationStep3Example(string kmsKeyId, string ddb // 3. Put an item into our table using the above client. // This item will be encrypted due to our PlaintextOverride configuration. + string partitionKeyName = "partition_key"; + string sortKeyName = "sort_key"; string encryptedAndSignedValue = MigrationUtils.ENCRYPTED_AND_SIGNED_VALUE; string signOnlyValue = MigrationUtils.SIGN_ONLY_VALUE; string doNothingValue = MigrationUtils.DO_NOTHING_VALUE; From dd2ce47bae5cf25b3f2f352e4a7804ec3a420af7 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Thu, 21 Aug 2025 17:02:17 -0700 Subject: [PATCH 15/25] auto commit --- .../PlaintextToAWSDBE/awsdbe/MigrationStep1.cs | 9 ++++++++- .../awsdbe/MigrationStep1Test.cs | 18 ------------------ .../awsdbe/MigrationStep2Test.cs | 18 ------------------ .../awsdbe/MigrationStep3Test.cs | 18 ------------------ 4 files changed, 8 insertions(+), 55 deletions(-) diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs index 0ae82ec5e..7199b604f 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs @@ -36,7 +36,14 @@ This table must be configured with the following public class MigrationStep1 { public static async Task MigrationStep1Example(string kmsKeyId, string ddbTableName, string partitionKeyValue, string sortKeyWriteValue, string sortKeyReadValue) - { + { + // 1. Create table configurations + // In this of migration we will use PlaintextOverride.FORCE_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ + // 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. var tableConfigs = Common.CreateTableConfigs(kmsKeyId, ddbTableName, PlaintextOverride.FORCE_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ); // 1. Create a new AWS SDK DynamoDb client using the TableEncryptionConfigs diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1Test.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1Test.cs index 605f89f61..73d500f4d 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1Test.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1Test.cs @@ -10,24 +10,6 @@ namespace Examples.migration.PlaintextToAWSDBE.awsdbe { - /* - Test for Migration Step 1: This tests the first step in the - plaintext-to-encrypted database migration. - - This test verifies that: - 1. Step 1 can successfully write plaintext items - 2. Step 1 can read items written by Step 0 (plaintext) - 3. Step 1 can read items written by itself (plaintext) - 4. Step 1 can read items written by Step 2 (encrypted) - 5. Step 1 can read items written by Step 3 (encrypted) - - 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 (N) - */ public class MigrationStep1Test { [Fact] diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2Test.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2Test.cs index 73865011b..ce021825d 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2Test.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2Test.cs @@ -10,24 +10,6 @@ namespace Examples.migration.PlaintextToAWSDBE.awsdbe { - /* - Test for Migration Step 2: This tests the second step in the - plaintext-to-encrypted database migration. - - This test verifies that: - 1. Step 2 can successfully write encrypted items - 2. Step 2 can read items written by Step 0 (plaintext) - 3. Step 2 can read items written by Step 1 (plaintext) - 4. Step 2 can read items written by itself (encrypted) - 5. Step 2 can read items written by Step 3 (encrypted) - - 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 (N) - */ public class MigrationStep2Test { [Fact] diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3Test.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3Test.cs index 2bf81c333..4cb76334c 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3Test.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3Test.cs @@ -11,24 +11,6 @@ namespace Examples.migration.PlaintextToAWSDBE.awsdbe { - /* - Test for Migration Step 3: This tests the final step in the - plaintext-to-encrypted database migration. - - This test verifies that: - 1. Step 3 can successfully write encrypted items - 2. Step 3 cannot read items written by Step 0 (plaintext) - should throw an exception - 3. Step 3 cannot read items written by Step 1 (plaintext) - should throw an exception - 4. Step 3 can read items written by Step 2 (encrypted) - 5. Step 3 can read items written by itself (encrypted) - - 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 (N) - */ public class MigrationStep3Test { [Fact] From 8588e0c1ac6e95b681f41f79a3d7d4d992d0d757 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Thu, 21 Aug 2025 20:46:41 -0700 Subject: [PATCH 16/25] auto commit --- Examples/runtimes/net/Examples.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Examples/runtimes/net/Examples.csproj b/Examples/runtimes/net/Examples.csproj index 573a10eb6..f57bdb388 100644 --- a/Examples/runtimes/net/Examples.csproj +++ b/Examples/runtimes/net/Examples.csproj @@ -11,6 +11,9 @@ + + + From 714780a9538f57d48627e30cb977063367a871bf Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Thu, 21 Aug 2025 20:53:26 -0700 Subject: [PATCH 17/25] auto commit --- .../src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0.cs index 1b5a14ab4..5e09c6b3e 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0.cs @@ -27,7 +27,7 @@ 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) + - Sort key is named "sort_key" with type (N) */ public class MigrationStep0 { From c519da6845e72f5a93c55c0ab1668485cf6f97f1 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Thu, 21 Aug 2025 22:05:47 -0700 Subject: [PATCH 18/25] auto commit --- .../PlaintextToAWSDBE/MigrationUtils.cs | 16 ++++++++-------- .../migration/PlaintextToAWSDBE/awsdbe/Common.cs | 4 ++-- .../PlaintextToAWSDBE/awsdbe/MigrationStep1.cs | 2 +- .../plaintext/MigrationStep0Test.cs | 16 ++++++++-------- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/MigrationUtils.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/MigrationUtils.cs index 5f4eb7508..891c0a4df 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/MigrationUtils.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/MigrationUtils.cs @@ -14,37 +14,37 @@ public class MigrationUtils public static readonly string ENCRYPTED_AND_SIGNED_VALUE = "this will be encrypted and signed"; public static readonly string SIGN_ONLY_VALUE = "this will never be encrypted, but it will be signed"; public static readonly string DO_NOTHING_VALUE = "this will never be encrypted nor signed"; - + // Verify that a returned item matches the expected values public static bool VerifyReturnedItem(GetItemResponse response, string partitionKeyValue, string sortKeyValue) - { + { var item = response.Item; - + 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")}"); } - + 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")}"); } - + if (!item.ContainsKey("attribute1") || item["attribute1"].S != ENCRYPTED_AND_SIGNED_VALUE) { throw new Exception($"attribute1 mismatch: expected {ENCRYPTED_AND_SIGNED_VALUE}, got {(item.ContainsKey("attribute1") ? item["attribute1"].S : "null")}"); } - + if (!item.ContainsKey("attribute2") || item["attribute2"].S != SIGN_ONLY_VALUE) { throw new Exception($"attribute2 mismatch: expected {SIGN_ONLY_VALUE}, got {(item.ContainsKey("attribute2") ? item["attribute2"].S : "null")}"); } - + if (!item.ContainsKey("attribute3") || item["attribute3"].S != DO_NOTHING_VALUE) { throw new Exception($"attribute3 mismatch: expected {DO_NOTHING_VALUE}, got {(item.ContainsKey("attribute3") ? item["attribute3"].S : "null")}"); } - + return true; } } diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/Common.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/Common.cs index a8fc3a122..912116d39 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/Common.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/Common.cs @@ -16,7 +16,7 @@ public static Dictionary CreateTableConfi var matProv = new MaterialProviders(new MaterialProvidersConfig()); var keyringInput = new CreateAwsKmsMrkMultiKeyringInput { Generator = kmsKeyId }; var kmsKeyring = matProv.CreateAwsKmsMrkMultiKeyring(keyringInput); - + // 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: @@ -33,7 +33,7 @@ public static Dictionary CreateTableConfi ["attribute2"] = CryptoAction.SIGN_ONLY, ["attribute3"] = CryptoAction.DO_NOTHING }; - + // Configure which attributes we expect to be excluded in the signature // when reading items. There are two options for configuring this: // diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs index 7199b604f..bcd78d5bd 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1.cs @@ -36,7 +36,7 @@ This table must be configured with the following public class MigrationStep1 { public static async Task MigrationStep1Example(string kmsKeyId, string ddbTableName, string partitionKeyValue, string sortKeyWriteValue, string sortKeyReadValue) - { + { // 1. Create table configurations // In this of migration we will use PlaintextOverride.FORCE_PLAINTEXT_WRITE_ALLOW_PLAINTEXT_READ // which means: diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0Test.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0Test.cs index 6675ae832..4a06c646c 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0Test.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/plaintext/MigrationStep0Test.cs @@ -41,28 +41,28 @@ public async Task TestMigrationStep0() // 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(() => + var exception = await Assert.ThrowsAsync(() => MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[2])); Assert.Contains("attribute1 mismatch", exception.Message); - + // Given: Step 3 has succeeded 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(() => + exception = await Assert.ThrowsAsync(() => MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[3])); Assert.Contains("attribute1 mismatch", exception.Message); - + // Cleanup foreach (var sortKey in sortKeys) { From 57934406a167f2371208653b478e5cf720cd2715 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Thu, 21 Aug 2025 22:06:02 -0700 Subject: [PATCH 19/25] auto commit --- .../PlaintextToAWSDBE/awsdbe/MigrationStep3Test.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3Test.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3Test.cs index 4cb76334c..f894ed5df 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3Test.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3Test.cs @@ -32,19 +32,19 @@ public async Task TestMigrationStep3() // Given: Step 2 has succeeded success = await MigrationStep2.MigrationStep2Example(kmsKeyID, tableName, partitionKey, sortKeys[2], sortKeys[2]); Assert.True(success, "MigrationStep2 should complete successfully"); - + // Successfully executes step 3 success = await MigrationStep3.MigrationStep3Example(kmsKeyID, tableName, partitionKey, sortKeys[3], sortKeys[3]); Assert.True(success, "MigrationStep3 should complete successfully"); - + // When: Execute Step 3 with sortReadValue=0, Then: should error out when reading plaintext items from Step 0 - var exception = await Assert.ThrowsAsync(() => + var exception = await Assert.ThrowsAsync(() => MigrationStep3.MigrationStep3Example(kmsKeyID, tableName, partitionKey, sortKeys[3], sortKeys[0])); - + Assert.Contains("encrypted item missing expected header and footer attributes", exception.Message.ToLower()); // When: Execute Step 3 with sortReadValue=1, Then: should error out when reading plaintext items from Step 1 - exception = await Assert.ThrowsAsync(() => + exception = await Assert.ThrowsAsync(() => MigrationStep3.MigrationStep3Example(kmsKeyID, tableName, partitionKey, sortKeys[3], sortKeys[1])); Assert.Contains("encrypted item missing expected header and footer attributes", exception.Message.ToLower()); From fd909c1669b92d6bb6b0be108641e154d021214b Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Fri, 22 Aug 2025 11:23:00 -0700 Subject: [PATCH 20/25] auto commit --- .../PlaintextToAWSDBE/awsdbe/MigrationStep1Test.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1Test.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1Test.cs index 73d500f4d..bfa817bec 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1Test.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1Test.cs @@ -20,14 +20,14 @@ public async Task TestMigrationStep1() string partitionKey = Guid.NewGuid().ToString(); string[] sortKeys = { "0", "1", "2", "3" }; - // Given: Step 0 has succeeded - bool success = await MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[0]); - Assert.True(success, "MigrationStep0 should complete successfully"); - // Successfully executes step 1 success = await MigrationStep1.MigrationStep1Example(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[1]); Assert.True(success, "MigrationStep1 should complete successfully"); + // Given: Step 0 has succeeded + bool success = await MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[0]); + Assert.True(success, "MigrationStep0 should complete successfully"); + // When: Execute Step 1 with sortReadValue=0, Then: Success (i.e. can read plaintext values from Step 0) success = await MigrationStep1.MigrationStep1Example(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[0]); Assert.True(success, "MigrationStep1 should be able to read items written by Step 0"); From f60600533b7f3d0a19a3c7bdf0df4a7f10d44a69 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Fri, 22 Aug 2025 11:25:48 -0700 Subject: [PATCH 21/25] auto commit --- .../awsdbe/MigrationStep2Test.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2Test.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2Test.cs index ce021825d..38788d1a8 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2Test.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2Test.cs @@ -20,22 +20,22 @@ public async Task TestMigrationStep2() string partitionKey = Guid.NewGuid().ToString(); string[] sortKeys = { "0", "1", "2", "3" }; - // Given: Step 0 has succeeded - bool success = await MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[0]); - Assert.True(success, "MigrationStep0 should complete successfully"); - - // Given: Step 1 has succeeded - success = await MigrationStep1.MigrationStep1Example(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[1]); - Assert.True(success, "MigrationStep1 should complete successfully"); - // Successfully executes step 2 success = await MigrationStep2.MigrationStep2Example(kmsKeyID, tableName, partitionKey, sortKeys[2], sortKeys[2]); Assert.True(success, "MigrationStep2 should complete successfully"); + // Given: Step 0 has succeeded + bool success = await MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[0]); + Assert.True(success, "MigrationStep0 should complete successfully"); + // When: Execute Step 2 with sortReadValue=0, Then: Success (i.e. can read plaintext values from Step 0) success = await MigrationStep2.MigrationStep2Example(kmsKeyID, tableName, partitionKey, sortKeys[2], sortKeys[0]); Assert.True(success, "MigrationStep2 should be able to read items written by Step 0"); + // Given: Step 1 has succeeded + success = await MigrationStep1.MigrationStep1Example(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[1]); + Assert.True(success, "MigrationStep1 should complete successfully"); + // When: Execute Step 2 with sortReadValue=1, Then: Success (i.e. can read plaintext values from Step 1) success = await MigrationStep2.MigrationStep2Example(kmsKeyID, tableName, partitionKey, sortKeys[2], sortKeys[1]); Assert.True(success, "MigrationStep2 should be able to read items written by Step 1"); From f2f1a39a4622412075430114ad70dbd71dbba36f Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Fri, 22 Aug 2025 11:41:41 -0700 Subject: [PATCH 22/25] auto commit --- .../awsdbe/MigrationStep3Test.cs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3Test.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3Test.cs index f894ed5df..30d281842 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3Test.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3Test.cs @@ -21,33 +21,33 @@ public async Task TestMigrationStep3() string partitionKey = Guid.NewGuid().ToString(); string[] sortKeys = { "0", "1", "2", "3" }; - // Given: Step 0 has succeeded - bool success = await MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[0]); - Assert.True(success, "MigrationStep0 should complete successfully"); - - // Given: Step 1 has succeeded - success = await MigrationStep1.MigrationStep1Example(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[1]); - Assert.True(success, "MigrationStep1 should complete successfully"); - - // Given: Step 2 has succeeded - success = await MigrationStep2.MigrationStep2Example(kmsKeyID, tableName, partitionKey, sortKeys[2], sortKeys[2]); - Assert.True(success, "MigrationStep2 should complete successfully"); - // Successfully executes step 3 success = await MigrationStep3.MigrationStep3Example(kmsKeyID, tableName, partitionKey, sortKeys[3], sortKeys[3]); Assert.True(success, "MigrationStep3 should complete successfully"); + // Given: Step 0 has succeeded + bool success = await MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[0]); + Assert.True(success, "MigrationStep0 should complete successfully"); + // When: Execute Step 3 with sortReadValue=0, Then: should error out when reading plaintext items from Step 0 var exception = await Assert.ThrowsAsync(() => MigrationStep3.MigrationStep3Example(kmsKeyID, tableName, partitionKey, sortKeys[3], sortKeys[0])); - Assert.Contains("encrypted item missing expected header and footer attributes", exception.Message.ToLower()); + // Given: Step 1 has succeeded + success = await MigrationStep1.MigrationStep1Example(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[1]); + Assert.True(success, "MigrationStep1 should complete successfully"); // When: Execute Step 3 with sortReadValue=1, Then: should error out when reading plaintext items from Step 1 exception = await Assert.ThrowsAsync(() => MigrationStep3.MigrationStep3Example(kmsKeyID, tableName, partitionKey, sortKeys[3], sortKeys[1])); Assert.Contains("encrypted item missing expected header and footer attributes", exception.Message.ToLower()); + // Given: Step 2 has succeeded + success = await MigrationStep2.MigrationStep2Example(kmsKeyID, tableName, partitionKey, sortKeys[2], sortKeys[2]); + Assert.True(success, "MigrationStep2 should complete successfully"); + + Assert.Contains("encrypted item missing expected header and footer attributes", exception.Message.ToLower()); + // When: Execute Step 3 with sortReadValue=2, Then: Success (i.e. can read encrypted values from Step 2) success = await MigrationStep3.MigrationStep3Example(kmsKeyID, tableName, partitionKey, sortKeys[3], sortKeys[2]); Assert.True(success, "MigrationStep3 should be able to read items written by Step 2"); From f46fde5e58e9f2c8698a9385d517b8228481f4a1 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Fri, 22 Aug 2025 12:54:50 -0700 Subject: [PATCH 23/25] auto commit --- .../src/migration/PlaintextToAWSDBE/README.md | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 Examples/runtimes/net/src/migration/PlaintextToAWSDBE/README.md diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/README.md b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/README.md new file mode 100644 index 000000000..c0fd2b0e5 --- /dev/null +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/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 056e20c12aa440686e42a7cf15582b40585fe2d6 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Mon, 25 Aug 2025 09:29:31 -0700 Subject: [PATCH 24/25] auto commit --- .../migration/PlaintextToAWSDBE/awsdbe/MigrationStep1Test.cs | 4 ++-- .../migration/PlaintextToAWSDBE/awsdbe/MigrationStep2Test.cs | 4 ++-- .../migration/PlaintextToAWSDBE/awsdbe/MigrationStep3Test.cs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1Test.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1Test.cs index bfa817bec..7822bd6c6 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1Test.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep1Test.cs @@ -21,11 +21,11 @@ public async Task TestMigrationStep1() string[] sortKeys = { "0", "1", "2", "3" }; // Successfully executes step 1 - success = await MigrationStep1.MigrationStep1Example(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[1]); + bool success = await MigrationStep1.MigrationStep1Example(kmsKeyID, tableName, partitionKey, sortKeys[1], sortKeys[1]); Assert.True(success, "MigrationStep1 should complete successfully"); // Given: Step 0 has succeeded - bool success = await MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[0]); + success = await MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[0]); Assert.True(success, "MigrationStep0 should complete successfully"); // When: Execute Step 1 with sortReadValue=0, Then: Success (i.e. can read plaintext values from Step 0) diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2Test.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2Test.cs index 38788d1a8..a1eb7075d 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2Test.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep2Test.cs @@ -21,11 +21,11 @@ public async Task TestMigrationStep2() string[] sortKeys = { "0", "1", "2", "3" }; // Successfully executes step 2 - success = await MigrationStep2.MigrationStep2Example(kmsKeyID, tableName, partitionKey, sortKeys[2], sortKeys[2]); + bool success = await MigrationStep2.MigrationStep2Example(kmsKeyID, tableName, partitionKey, sortKeys[2], sortKeys[2]); Assert.True(success, "MigrationStep2 should complete successfully"); // Given: Step 0 has succeeded - bool success = await MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[0]); + success = await MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[0]); Assert.True(success, "MigrationStep0 should complete successfully"); // When: Execute Step 2 with sortReadValue=0, Then: Success (i.e. can read plaintext values from Step 0) diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3Test.cs b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3Test.cs index 30d281842..52c3f9838 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3Test.cs +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/awsdbe/MigrationStep3Test.cs @@ -22,11 +22,11 @@ public async Task TestMigrationStep3() string[] sortKeys = { "0", "1", "2", "3" }; // Successfully executes step 3 - success = await MigrationStep3.MigrationStep3Example(kmsKeyID, tableName, partitionKey, sortKeys[3], sortKeys[3]); + bool success = await MigrationStep3.MigrationStep3Example(kmsKeyID, tableName, partitionKey, sortKeys[3], sortKeys[3]); Assert.True(success, "MigrationStep3 should complete successfully"); // Given: Step 0 has succeeded - bool success = await MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[0]); + success = await MigrationStep0.MigrationStep0Example(tableName, partitionKey, sortKeys[0], sortKeys[0]); Assert.True(success, "MigrationStep0 should complete successfully"); // When: Execute Step 3 with sortReadValue=0, Then: should error out when reading plaintext items from Step 0 From c293cfd294859536688e1baba9517528a5b92af1 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Mon, 25 Aug 2025 09:34:58 -0700 Subject: [PATCH 25/25] auto commit --- Examples/runtimes/net/src/migration/PlaintextToAWSDBE/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/README.md b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/README.md index c0fd2b0e5..31170c101 100644 --- a/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/README.md +++ b/Examples/runtimes/net/src/migration/PlaintextToAWSDBE/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.