From 888d1d5733bc1ae13a137269f3b3ca5ca9b8f940 Mon Sep 17 00:00:00 2001 From: Ryan Emery Date: Wed, 8 Oct 2025 15:42:15 -0700 Subject: [PATCH 1/6] Updates to a V3 supported Ruby --- test-server/Makefile | 2 +- test-server/ruby-v2-server/Gemfile.lock | 4 ++-- test-server/ruby-v2-server/local-ruby-sdk | 2 +- test-server/ruby-v3-server/Gemfile.lock | 4 ++-- test-server/ruby-v3-server/app.rb | 4 ++-- test-server/ruby-v3-server/lib/client_manager.rb | 2 +- test-server/ruby-v3-server/local-ruby-sdk | 2 +- test-server/specification | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/test-server/Makefile b/test-server/Makefile index 207873c6..d2b91a5d 100644 --- a/test-server/Makefile +++ b/test-server/Makefile @@ -8,7 +8,7 @@ all: start-servers run-tests # CI target for GitHub Actions ci: start-servers run-tests stop-servers -SERVER_DIRS := $(shell find . -maxdepth 1 -type d -name '*-server' | sed 's|^\./||' | $(if $(FILTER),grep $(FILTER),cat) | sort) +SERVER_DIRS := $(shell find . -maxdepth 1 -type d -name '*-server' | sed 's|^\./||' | $(if $(FILTER),grep -E "$$(echo '$(FILTER)' | sed 's/,/|/g')",cat) | sort) # SERVER_DIRS := cpp-v3-server START_SERVER_TARGETS := $(addprefix start-, $(SERVER_DIRS)) diff --git a/test-server/ruby-v2-server/Gemfile.lock b/test-server/ruby-v2-server/Gemfile.lock index 660aadd5..04815a40 100644 --- a/test-server/ruby-v2-server/Gemfile.lock +++ b/test-server/ruby-v2-server/Gemfile.lock @@ -1,14 +1,14 @@ PATH remote: local-ruby-sdk/gems/aws-sdk-kms specs: - aws-sdk-kms (1.112.0) + aws-sdk-kms (1.113.0) aws-sdk-core (~> 3, >= 3.231.0) aws-sigv4 (~> 1.5) PATH remote: local-ruby-sdk/gems/aws-sdk-s3 specs: - aws-sdk-s3 (1.199.0) + aws-sdk-s3 (1.199.1) aws-sdk-core (~> 3, >= 3.231.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) diff --git a/test-server/ruby-v2-server/local-ruby-sdk b/test-server/ruby-v2-server/local-ruby-sdk index e129cf37..ba15842f 160000 --- a/test-server/ruby-v2-server/local-ruby-sdk +++ b/test-server/ruby-v2-server/local-ruby-sdk @@ -1 +1 @@ -Subproject commit e129cf37c170254ebb631782cae145040cde6d0b +Subproject commit ba15842f5b5d9cf6855a1023753d32eb8606bbef diff --git a/test-server/ruby-v3-server/Gemfile.lock b/test-server/ruby-v3-server/Gemfile.lock index ae04e5fd..9edf1f5d 100644 --- a/test-server/ruby-v3-server/Gemfile.lock +++ b/test-server/ruby-v3-server/Gemfile.lock @@ -1,14 +1,14 @@ PATH remote: local-ruby-sdk/gems/aws-sdk-kms specs: - aws-sdk-kms (1.112.0) + aws-sdk-kms (1.113.0) aws-sdk-core (~> 3, >= 3.231.0) aws-sigv4 (~> 1.5) PATH remote: local-ruby-sdk/gems/aws-sdk-s3 specs: - aws-sdk-s3 (1.199.0) + aws-sdk-s3 (1.199.1) aws-sdk-core (~> 3, >= 3.231.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) diff --git a/test-server/ruby-v3-server/app.rb b/test-server/ruby-v3-server/app.rb index abc25932..2633c5dc 100644 --- a/test-server/ruby-v3-server/app.rb +++ b/test-server/ruby-v3-server/app.rb @@ -129,7 +129,7 @@ def initialize metadata: response_metadata }.to_json - rescue Aws::S3::EncryptionV2::Errors::EncryptionError => e + rescue Aws::S3::EncryptionV2::Errors::EncryptionError, Aws::S3::EncryptionV3::Errors::EncryptionError => e S3ECLogger.log_error(e, { endpoint: '/put', error_category: 'EncryptionError' }, @request_id) ErrorHandlers.send_s3_encryption_client_error(self, e.message) rescue StandardError => e @@ -198,7 +198,7 @@ def initialize content_type 'application/octet-stream' body - rescue Aws::S3::EncryptionV2::Errors::DecryptionError => e + rescue Aws::S3::EncryptionV2::Errors::DecryptionError, Aws::S3::EncryptionV3::Errors::DecryptionError => e S3ECLogger.log_error(e, { endpoint: '/get', error_category: 'DecryptionError' }, @request_id) ErrorHandlers.send_s3_encryption_client_error(self, e.message) rescue StandardError => e diff --git a/test-server/ruby-v3-server/lib/client_manager.rb b/test-server/ruby-v3-server/lib/client_manager.rb index d3b12b23..261f19e6 100644 --- a/test-server/ruby-v3-server/lib/client_manager.rb +++ b/test-server/ruby-v3-server/lib/client_manager.rb @@ -32,7 +32,7 @@ def create_client(config) # Create the S3 encryption client s3_client = Aws::S3::Client.new(region: 'us-west-2') - encryption_client = Aws::S3::EncryptionV2::Client.new( + encryption_client = Aws::S3::EncryptionV3::Client.new( client: s3_client, **encryption_config ) diff --git a/test-server/ruby-v3-server/local-ruby-sdk b/test-server/ruby-v3-server/local-ruby-sdk index e129cf37..ba15842f 160000 --- a/test-server/ruby-v3-server/local-ruby-sdk +++ b/test-server/ruby-v3-server/local-ruby-sdk @@ -1 +1 @@ -Subproject commit e129cf37c170254ebb631782cae145040cde6d0b +Subproject commit ba15842f5b5d9cf6855a1023753d32eb8606bbef diff --git a/test-server/specification b/test-server/specification index c534aee8..0ae23623 160000 --- a/test-server/specification +++ b/test-server/specification @@ -1 +1 @@ -Subproject commit c534aee8c2d34c462dfac6ab21ae59467dcedd68 +Subproject commit 0ae23623ff4b3330da0c58890fbc032a7d378d08 From 61fca1663b2ded657332ad8cd6199068f97750b0 Mon Sep 17 00:00:00 2001 From: Ryan Emery Date: Thu, 9 Oct 2025 09:25:55 -0700 Subject: [PATCH 2/6] Trying to make it work --- .../s3/ExhaustiveRoundTripTests1_25.java | 1348 +++++++++++++++++ test-server/model/client.smithy | 6 + test-server/model/object.smithy | 33 + 3 files changed, 1387 insertions(+) create mode 100644 test-server/java-tests/src/it/java/software/amazon/encryption/s3/ExhaustiveRoundTripTests1_25.java diff --git a/test-server/java-tests/src/it/java/software/amazon/encryption/s3/ExhaustiveRoundTripTests1_25.java b/test-server/java-tests/src/it/java/software/amazon/encryption/s3/ExhaustiveRoundTripTests1_25.java new file mode 100644 index 00000000..e1e7d098 --- /dev/null +++ b/test-server/java-tests/src/it/java/software/amazon/encryption/s3/ExhaustiveRoundTripTests1_25.java @@ -0,0 +1,1348 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.encryption.s3; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.amazonaws.services.s3.model.KMSEncryptionMaterials; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import software.amazon.encryption.s3.model.*; +import software.amazon.encryption.s3.client.S3ECTestServerClient; + +import com.amazonaws.services.s3.AmazonS3Encryption; +import com.amazonaws.services.s3.AmazonS3EncryptionClient; +import com.amazonaws.services.s3.model.CryptoConfiguration; +import com.amazonaws.services.s3.model.CryptoMode; +import com.amazonaws.services.s3.model.CryptoStorageMode; +import com.amazonaws.services.s3.model.EncryptionMaterialsProvider; +import com.amazonaws.services.s3.model.KMSEncryptionMaterialsProvider; + +import static software.amazon.encryption.s3.TestUtils.*; + +/** + * Exhaustive tests for S3 Encryption Client round-trip operations. + * These tests cover various combinations of client versions, commitment policies, and encryption modes. + * + * Tests are based on the exhaustive test matrix defined at: + * https://tiny.amazon.com/3xnzwczl/loopcloumicrpeyJ3 + * + * Tests 1-25 are included in this file. + */ +public class ExhaustiveRoundTripTests1_25 { + + @BeforeAll + public static void setup() { + TestUtils.setupTestServers(); + } + + // Begin Exhaustive tests defined here: + // https://tiny.amazon.com/3xnzwczl/loopcloumicrpeyJ3 + + + // Exhaustive test 1 + // Outcome Version Operation Policy Content Encryption + // Fail Current Decrypt null KC-GCM + + @ParameterizedTest(name = "{displayName} for Encrypt: {0}, Decrypt: {1}") + @MethodSource("TestUtils#crossLanguageClients") + public void GIVEN_DataEncryptedWithKC_AND_CurrentClientDecrypting_WHEN_Decrypt_THEN_Fail(TestUtils.LanguageServerTarget encLang, TestUtils.LanguageServerTarget decLang) { + // Given: encrypt language is either an improved version or a transition version + if (!TestUtils.IMPROVED_VERSIONS.contains(encLang.getLanguageName()) || !TestUtils.TRANSITION_VERSIONS.contains(encLang.getLanguageName())) { + return; + } + + // Given: decrypt language is a current version + if (!TestUtils.CURRENT_VERSIONS.contains(decLang.getLanguageName())) { + return; + } + + S3ECTestServerClient encClient = TestUtils.testServerClientFor(encLang); + final String objectKey = "encrypt-kc-decrypt-current-test-key-" + encLang; + final String input = "simple-test-input"; + KeyMaterial kmsKeyArn = KeyMaterial.builder() + .kmsKeyId(TestUtils.KMS_KEY_ARN) + .build(); + CreateClientOutput encClientOutput = encClient.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT) + .build()) + .build()); + String encS3ECId = encClientOutput.getClientId(); + // Given: object encrypted with key commitment + encClient.putObject(PutObjectInput.builder() + .clientID(encS3ECId) + .key(objectKey) + .bucket(TestUtils.BUCKET) + .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) + .build()); + S3ECTestServerClient decClient = TestUtils.testServerClientFor(decLang); + CreateClientOutput decClientOutput = decClient.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .keyMaterial(kmsKeyArn).build()) + .build()); + String decS3ECId = decClientOutput.getClientId(); + + // Then: Fails + try { + decClient.getObject(GetObjectInput.builder() + .clientID(decS3ECId) + .bucket(TestUtils.BUCKET) + .key(objectKey) + .build()); + fail("Expected Exception"); + } catch (S3EncryptionClientError e) { + assertTrue(e.getMessage().contains("TODO: Expected error message for decrypting unrecognized alg suite")); + } + } + + // Exhaustive test 2 + // Outcome Version Operation Policy Content Encryption + // Pass Improved Decrypt ForbidEncryptAllowDecrypt CBC + + @ParameterizedTest(name = "{displayName} for Encrypt: Java-V1, Decrypt: {0}") + @MethodSource("TestUtils#improvedClientsForTest") + public void GIVEN_CBCEncryptedData_AND_ImprovedClientDecryptingWithForbidEncryptAllowDecrypt_WHEN_Decrypt_THEN_Pass( + TestUtils.LanguageServerTarget language + ) { + S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); + final String objectKey = "test-key-kms-v1-" + language; + final String input = "simple-test-input"; + KeyMaterial kmsKeyArn = KeyMaterial.builder() + .kmsKeyId(TestUtils.KMS_KEY_ARN) + .build(); + CreateClientOutput output1 = client.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .enableLegacyWrappingAlgorithms(true) + .keyMaterial(kmsKeyArn) + .build()) + .build()); + String s3ECId = output1.getClientId(); + + // Create the object using the old client + // V1 Client + EncryptionMaterialsProvider materialsProvider = new KMSEncryptionMaterialsProvider(TestUtils.KMS_KEY_ARN); + + CryptoConfiguration v1Config = + new CryptoConfiguration(CryptoMode.AuthenticatedEncryption) + .withStorageMode(CryptoStorageMode.ObjectMetadata) + .withAwsKmsRegion(TestUtils.KMS_REGION); + + AmazonS3Encryption v1Client = AmazonS3EncryptionClient.encryptionBuilder() + .withCryptoConfiguration(v1Config) + .withEncryptionMaterials(materialsProvider) + .build(); + + v1Client.putObject(TestUtils.BUCKET, objectKey, input); + + S3ECTestServerClient decClient = TestUtils.testServerClientFor(language); + CreateClientOutput decClientOutput = decClient.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .keyMaterial(kmsKeyArn).build()) + .build()); + String decS3ECId = decClientOutput.getClientId(); + + // When: decrypt KC object with a current version client + GetObjectOutput output = decClient.getObject(GetObjectInput.builder() + .clientID(decS3ECId) + .bucket(TestUtils.BUCKET) + .key(objectKey) + .build()); + + // Then: Pass + client.getObject(GetObjectInput.builder() + .clientID(s3ECId) + .bucket(TestUtils.BUCKET) + .key(objectKey) + .build()); + } + + // Exhaustive test 3 + // Outcome Version Operation Policy Content Encryption + // Pass Improved Decrypt ForbidEncryptAllowDecrypt GCM + + @ParameterizedTest(name = "{displayName} for Encrypt: Java-V1-GCM, Decrypt: {0}") + @MethodSource("TestUtils#improvedClientsForTest") + public void GIVEN_GCMEncryptedData_AND_ImprovedClientDecryptingWithForbidEncryptAllowDecrypt_WHEN_Decrypt_THEN_Pass( + String language + ) { + S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); + final String objectKey = "test-key-kms-v1-gcm-" + language; + final String input = "simple-test-input"; + KeyMaterial kmsKeyArn = KeyMaterial.builder() + .kmsKeyId(TestUtils.KMS_KEY_ARN) + .build(); + CreateClientOutput output1 = client.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .enableLegacyWrappingAlgorithms(true) + .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT) + .build()) + .build()); + String s3ECId = output1.getClientId(); + + // Create the object using the old client with GCM encryption + // V1 Client with GCM + EncryptionMaterialsProvider materialsProvider = new KMSEncryptionMaterialsProvider(TestUtils.KMS_KEY_ARN); + + CryptoConfiguration v1Config = + new CryptoConfiguration(CryptoMode.StrictAuthenticatedEncryption) // StrictAuthenticatedEncryption uses GCM + .withStorageMode(CryptoStorageMode.ObjectMetadata) + .withAwsKmsRegion(TestUtils.KMS_REGION); + + AmazonS3Encryption v1Client = AmazonS3EncryptionClient.encryptionBuilder() + .withCryptoConfiguration(v1Config) + .withEncryptionMaterials(materialsProvider) + .build(); + + v1Client.putObject(TestUtils.BUCKET, objectKey, input); + + // When: decrypt GCM object with an improved version client + GetObjectOutput output = client.getObject(GetObjectInput.builder() + .clientID(s3ECId) + .bucket(TestUtils.BUCKET) + .key(objectKey) + .build()); + + // Then: Pass + assertEquals(input, new String(output.getBody().array())); + } + + // Exhaustive test 4 + // Outcome Version Operation Policy Content Encryption + // Pass Improved Decrypt ForbidEncryptAllowDecrypt KC-GCM + + @ParameterizedTest(name = "{displayName} for Encrypt: {0}, Decrypt: {1}") + @MethodSource("TestUtils#crossLanguageClients") + public void GIVEN_KCGCMEncryptedData_AND_ImprovedClientDecryptingWithForbidEncryptAllowDecrypt_WHEN_Decrypt_THEN_Pass( + TestUtils.LanguageServerTarget encLang, TestUtils.LanguageServerTarget decLang + ) { + // Given: encrypt language is an improved version or a transition version + if (!TestUtils.IMPROVED_VERSIONS.contains(encLang.getLanguageName()) || !TestUtils.TRANSITION_VERSIONS.contains(encLang.getLanguageName())) { + return; + } + + // Given: decrypt language is an improved version + if (!TestUtils.IMPROVED_VERSIONS.contains(decLang.getLanguageName())) { + return; + } + + S3ECTestServerClient encClient = TestUtils.testServerClientFor(encLang); + final String objectKey = "encrypt-kc-gcm-decrypt-improved-test-key-" + encLang; + final String input = "simple-test-input"; + KeyMaterial kmsKeyArn = KeyMaterial.builder() + .kmsKeyId(TestUtils.KMS_KEY_ARN) + .build(); + CreateClientOutput encClientOutput = encClient.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT) + .build()) + .build()); + String encS3ECId = encClientOutput.getClientId(); + + // Given: object encrypted with key commitment + encClient.putObject(PutObjectInput.builder() + .clientID(encS3ECId) + .key(objectKey) + .bucket(TestUtils.BUCKET) + .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) + .build()); + + S3ECTestServerClient decClient = TestUtils.testServerClientFor(decLang); + CreateClientOutput decClientOutput = decClient.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT) + .build()) + .build()); + String decS3ECId = decClientOutput.getClientId(); + + // When: decrypt KC-GCM object with an improved version client with ForbidEncryptAllowDecrypt policy + GetObjectOutput output = decClient.getObject(GetObjectInput.builder() + .clientID(decS3ECId) + .bucket(TestUtils.BUCKET) + .key(objectKey) + .build()); + + // Then: Pass + assertEquals(input, StandardCharsets.UTF_8.decode(output.getBody()).toString()); + } + + + // Exhaustive test 5 + // Outcome Version Operation Policy Content Encryption + // Fail Improved Decrypt null CBC + + @ParameterizedTest(name = "{displayName} for Encrypt: Java-V1-CBC, Decrypt: {0}") + @MethodSource("TestUtils#improvedClientsForTest") + public void GIVEN_CBCEncryptedData_AND_ImprovedClientDecryptingWithNullPolicy_WHEN_Decrypt_THEN_Fail( + String language + ) { + // Given: decrypt language is an improved version + if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { + return; + } + + S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); + final String objectKey = "test-key-kms-v1-cbc-null-policy-" + language; + final String input = "simple-test-input"; + KeyMaterial kmsKeyArn = KeyMaterial.builder() + .kmsKeyId(TestUtils.KMS_KEY_ARN) + .build(); + + // Create client with null commitment policy (not explicitly set) + CreateClientOutput output1 = client.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .enableLegacyWrappingAlgorithms(true) + .keyMaterial(kmsKeyArn) + // No commitment policy set - defaults to null + .build()) + .build()); + String s3ECId = output1.getClientId(); + + // Create the object using the old client with CBC encryption + // V1 Client with CBC + EncryptionMaterialsProvider materialsProvider = new KMSEncryptionMaterialsProvider(TestUtils.KMS_KEY_ARN); + + CryptoConfiguration v1Config = + new CryptoConfiguration(CryptoMode.AuthenticatedEncryption) // AuthenticatedEncryption uses CBC + .withStorageMode(CryptoStorageMode.ObjectMetadata) + .withAwsKmsRegion(TestUtils.KMS_REGION); + + AmazonS3Encryption v1Client = AmazonS3EncryptionClient.encryptionBuilder() + .withCryptoConfiguration(v1Config) + .withEncryptionMaterials(materialsProvider) + .build(); + + v1Client.putObject(TestUtils.BUCKET, objectKey, input); + + // When: decrypt CBC object with an improved version client with null policy + // Then: Fails + try { + client.getObject(GetObjectInput.builder() + .clientID(s3ECId) + .bucket(TestUtils.BUCKET) + .key(objectKey) + .build()); + fail("Expected Exception"); + } catch (S3EncryptionClientError e) { + assertTrue(e.getMessage().contains("TODO: Expected error message for decrypting with null policy")); + } + } + + // Exhaustive test 6 + // Outcome Version Operation Policy Content Encryption + // Fail Improved Decrypt null GCM + + @ParameterizedTest(name = "{displayName} for Encrypt: Java-V1-GCM, Decrypt: {0}") + @MethodSource("TestUtils#improvedClientsForTest") + public void GIVEN_GCMEncryptedData_AND_ImprovedClientDecryptingWithNullPolicy_WHEN_Decrypt_THEN_Fail( + String language + ) { + // Given: decrypt language is an improved version + if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { + return; + } + + S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); + final String objectKey = "test-key-kms-v1-gcm-null-policy-" + language; + final String input = "simple-test-input"; + KeyMaterial kmsKeyArn = KeyMaterial.builder() + .kmsKeyId(TestUtils.KMS_KEY_ARN) + .build(); + + // Create client with null commitment policy (not explicitly set) + CreateClientOutput output1 = client.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .enableLegacyWrappingAlgorithms(true) + .keyMaterial(kmsKeyArn) + // No commitment policy set - defaults to null + .build()) + .build()); + String s3ECId = output1.getClientId(); + + // Create the object using the old client with GCM encryption + // V1 Client with GCM + EncryptionMaterialsProvider materialsProvider = new KMSEncryptionMaterialsProvider(TestUtils.KMS_KEY_ARN); + + CryptoConfiguration v1Config = + new CryptoConfiguration(CryptoMode.StrictAuthenticatedEncryption) // StrictAuthenticatedEncryption uses GCM + .withStorageMode(CryptoStorageMode.ObjectMetadata) + .withAwsKmsRegion(TestUtils.KMS_REGION); + + AmazonS3Encryption v1Client = AmazonS3EncryptionClient.encryptionBuilder() + .withCryptoConfiguration(v1Config) + .withEncryptionMaterials(materialsProvider) + .build(); + + v1Client.putObject(TestUtils.BUCKET, objectKey, input); + + // When: decrypt GCM object with an improved version client with null policy + // Then: Fails + try { + client.getObject(GetObjectInput.builder() + .clientID(s3ECId) + .bucket(TestUtils.BUCKET) + .key(objectKey) + .build()); + fail("Expected Exception"); + } catch (S3EncryptionClientError e) { + assertTrue(e.getMessage().contains("TODO: Expected error message for decrypting with null policy")); + } + } + + // Exhaustive test 7 + // Outcome Version Operation Policy Content Encryption + // Pass Improved Decrypt null KC-GCM + + @ParameterizedTest(name = "{displayName} for Encrypt: {0}, Decrypt: {1}") + @MethodSource("TestUtils#crossLanguageClients") + public void GIVEN_KCGCMEncryptedData_AND_ImprovedClientDecryptingWithNullPolicy_WHEN_Decrypt_THEN_Pass( + TestUtils.LanguageServerTarget encLang, TestUtils.LanguageServerTarget decLang + ) { + // Given: encrypt language is an improved version + if (!TestUtils.IMPROVED_VERSIONS.contains(encLang.getLanguageName())) { + return; + } + + // Given: decrypt language is an improved version + if (!TestUtils.IMPROVED_VERSIONS.contains(decLang.getLanguageName())) { + return; + } + + S3ECTestServerClient encClient = TestUtils.testServerClientFor(encLang); + final String objectKey = "encrypt-kc-gcm-decrypt-improved-null-policy-" + encLang; + final String input = "simple-test-input"; + KeyMaterial kmsKeyArn = KeyMaterial.builder() + .kmsKeyId(TestUtils.KMS_KEY_ARN) + .build(); + CreateClientOutput encClientOutput = encClient.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT) + .build()) + .build()); + String encS3ECId = encClientOutput.getClientId(); + + // Given: object encrypted with key commitment + encClient.putObject(PutObjectInput.builder() + .clientID(encS3ECId) + .key(objectKey) + .bucket(TestUtils.BUCKET) + .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) + .build()); + + S3ECTestServerClient decClient = TestUtils.testServerClientFor(decLang); + // Create client with null commitment policy (not explicitly set) + CreateClientOutput decClientOutput = decClient.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .keyMaterial(kmsKeyArn) + // No commitment policy set - defaults to null + .build()) + .build()); + String decS3ECId = decClientOutput.getClientId(); + + // When: decrypt KC-GCM object with an improved version client with null policy + GetObjectOutput output = decClient.getObject(GetObjectInput.builder() + .clientID(decS3ECId) + .bucket(TestUtils.BUCKET) + .key(objectKey) + .build()); + + // Then: Pass + assertEquals(input, StandardCharsets.UTF_8.decode(output.getBody()).toString()); + } + + + // Exhaustive test 8 + // Outcome Version Operation Policy Content Encryption + // Pass Improved Decrypt RequireEncryptAllowDecrypt CBC + + @ParameterizedTest(name = "{displayName} for Encrypt: Java-V1-CBC, Decrypt: {0}") + @MethodSource("TestUtils#improvedClientsForTest") + public void GIVEN_CBCEncryptedData_AND_ImprovedClientDecryptingWithRequireEncryptAllowDecrypt_WHEN_Decrypt_THEN_Pass( + String language + ) { + // Given: decrypt language is an improved version + if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { + return; + } + + S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); + final String objectKey = "test-key-kms-v1-cbc-require-encrypt-allow-decrypt-" + language; + final String input = "simple-test-input"; + KeyMaterial kmsKeyArn = KeyMaterial.builder() + .kmsKeyId(KMS_KEY_ARN) + .build(); + + // Create client with RequireEncryptAllowDecrypt commitment policy + CreateClientOutput output1 = client.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .enableLegacyWrappingAlgorithms(true) + .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT) + .build()) + .build()); + String s3ECId = output1.getClientId(); + + // Create the object using the old client with CBC encryption + // V1 Client with CBC + EncryptionMaterialsProvider materialsProvider = new KMSEncryptionMaterialsProvider(KMS_KEY_ARN); + + CryptoConfiguration v1Config = + new CryptoConfiguration(CryptoMode.AuthenticatedEncryption) // AuthenticatedEncryption uses CBC + .withStorageMode(CryptoStorageMode.ObjectMetadata) + .withAwsKmsRegion(KMS_REGION); + + AmazonS3Encryption v1Client = AmazonS3EncryptionClient.encryptionBuilder() + .withCryptoConfiguration(v1Config) + .withEncryptionMaterials(materialsProvider) + .build(); + + v1Client.putObject(BUCKET, objectKey, input); + + // When: decrypt CBC object with an improved version client with RequireEncryptAllowDecrypt policy + GetObjectOutput output = client.getObject(GetObjectInput.builder() + .clientID(s3ECId) + .bucket(BUCKET) + .key(objectKey) + .build()); + + // Then: Pass + assertEquals(input, new String(output.getBody().array())); + } + + // Exhaustive test 9 + // Outcome Version Operation Policy Content Encryption + // Pass Improved Decrypt RequireEncryptAllowDecrypt GCM + + @ParameterizedTest(name = "{displayName} for Encrypt: Java-V1-GCM, Decrypt: {0}") + @MethodSource("TestUtils#improvedClientsForTest") + public void GIVEN_GCMEncryptedData_AND_ImprovedClientDecryptingWithRequireEncryptAllowDecrypt_WHEN_Decrypt_THEN_Pass( + String language + ) { + // Given: decrypt language is an improved version + if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { + return; + } + + S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); + final String objectKey = "test-key-kms-v1-gcm-require-encrypt-allow-decrypt-" + language; + final String input = "simple-test-input"; + KeyMaterial kmsKeyArn = KeyMaterial.builder() + .kmsKeyId(KMS_KEY_ARN) + .build(); + + // Create client with RequireEncryptAllowDecrypt commitment policy + CreateClientOutput output1 = client.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .enableLegacyWrappingAlgorithms(true) + .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT) + .build()) + .build()); + String s3ECId = output1.getClientId(); + + // Create the object using the old client with GCM encryption + // V1 Client with GCM + EncryptionMaterialsProvider materialsProvider = new KMSEncryptionMaterialsProvider(KMS_KEY_ARN); + + CryptoConfiguration v1Config = + new CryptoConfiguration(CryptoMode.StrictAuthenticatedEncryption) // StrictAuthenticatedEncryption uses GCM + .withStorageMode(CryptoStorageMode.ObjectMetadata) + .withAwsKmsRegion(KMS_REGION); + + AmazonS3Encryption v1Client = AmazonS3EncryptionClient.encryptionBuilder() + .withCryptoConfiguration(v1Config) + .withEncryptionMaterials(materialsProvider) + .build(); + + v1Client.putObject(BUCKET, objectKey, input); + + // When: decrypt GCM object with an improved version client with RequireEncryptAllowDecrypt policy + GetObjectOutput output = client.getObject(GetObjectInput.builder() + .clientID(s3ECId) + .bucket(BUCKET) + .key(objectKey) + .build()); + + // Then: Pass + assertEquals(input, new String(output.getBody().array())); + } + + // Exhaustive test 10 + // Outcome Version Operation Policy Content Encryption + // Pass Improved Decrypt RequireEncryptAllowDecrypt KC-GCM + + @ParameterizedTest(name = "{displayName} for Encrypt: {0}, Decrypt: {1}") + @MethodSource("TestUtils#crossLanguageClients") + public void GIVEN_KCGCMEncryptedData_AND_ImprovedClientDecryptingWithRequireEncryptAllowDecrypt_WHEN_Decrypt_THEN_Pass( + TestUtils.LanguageServerTarget encLang, TestUtils.LanguageServerTarget decLang + ) { + // Given: encrypt language is an improved version + if (!TestUtils.IMPROVED_VERSIONS.contains(encLang.getLanguageName())) { + return; + } + + // Given: decrypt language is an improved version + if (!TestUtils.IMPROVED_VERSIONS.contains(decLang.getLanguageName())) { + return; + } + + S3ECTestServerClient encClient = TestUtils.testServerClientFor(encLang); + final String objectKey = "encrypt-kc-gcm-decrypt-improved-require-encrypt-allow-decrypt-" + encLang; + final String input = "simple-test-input"; + KeyMaterial kmsKeyArn = KeyMaterial.builder() + .kmsKeyId(KMS_KEY_ARN) + .build(); + CreateClientOutput encClientOutput = encClient.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT) + .build()) + .build()); + String encS3ECId = encClientOutput.getClientId(); + + // Given: object encrypted with key commitment + encClient.putObject(PutObjectInput.builder() + .clientID(encS3ECId) + .key(objectKey) + .bucket(BUCKET) + .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) + .build()); + + S3ECTestServerClient decClient = testServerClientFor(decLang); + // Create client with RequireEncryptAllowDecrypt commitment policy + CreateClientOutput decClientOutput = decClient.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT) + .build()) + .build()); + String decS3ECId = decClientOutput.getClientId(); + + // When: decrypt KC-GCM object with an improved version client with RequireEncryptAllowDecrypt policy + GetObjectOutput output = decClient.getObject(GetObjectInput.builder() + .clientID(decS3ECId) + .bucket(BUCKET) + .key(objectKey) + .build()); + + // Then: Pass + assertEquals(input, StandardCharsets.UTF_8.decode(output.getBody()).toString()); + } + + // Exhaustive test 11 + // Outcome Version Operation Policy Content Encryption + // Fail Improved Decrypt RequireEncryptRequireDecrypt CBC + + @ParameterizedTest(name = "{displayName} for Encrypt: Java-V1-CBC, Decrypt: {0}") + @MethodSource("TestUtils#improvedClientsForTest") + public void GIVEN_CBCEncryptedData_AND_ImprovedClientDecryptingWithRequireEncryptRequireDecrypt_WHEN_Decrypt_THEN_Fail( + String language + ) { + // Given: decrypt language is an improved version + if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { + return; + } + + S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); + final String objectKey = "test-key-kms-v1-cbc-require-encrypt-require-decrypt-" + language; + final String input = "simple-test-input"; + KeyMaterial kmsKeyArn = KeyMaterial.builder() + .kmsKeyId(KMS_KEY_ARN) + .build(); + + // Create client with RequireEncryptRequireDecrypt commitment policy + CreateClientOutput output1 = client.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .enableLegacyWrappingAlgorithms(true) + .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT) + .build()) + .build()); + String s3ECId = output1.getClientId(); + + // Create the object using the old client with CBC encryption + // V1 Client with CBC + EncryptionMaterialsProvider materialsProvider = new KMSEncryptionMaterialsProvider(KMS_KEY_ARN); + + CryptoConfiguration v1Config = + new CryptoConfiguration(CryptoMode.AuthenticatedEncryption) // AuthenticatedEncryption uses CBC + .withStorageMode(CryptoStorageMode.ObjectMetadata) + .withAwsKmsRegion(KMS_REGION); + + AmazonS3Encryption v1Client = AmazonS3EncryptionClient.encryptionBuilder() + .withCryptoConfiguration(v1Config) + .withEncryptionMaterials(materialsProvider) + .build(); + + v1Client.putObject(BUCKET, objectKey, input); + + // When: decrypt CBC object with an improved version client with RequireEncryptRequireDecrypt policy + // Then: Fails + try { + client.getObject(GetObjectInput.builder() + .clientID(s3ECId) + .bucket(BUCKET) + .key(objectKey) + .build()); + fail("Expected Exception"); + } catch (S3EncryptionClientError e) { + assertTrue(e.getMessage().contains("TODO: Expected error message for decrypting with RequireEncryptRequireDecrypt policy")); + } + } + + // Exhaustive test 12 + // Outcome Version Operation Policy Content Encryption + // Fail Improved Decrypt RequireEncryptRequireDecrypt GCM + + @ParameterizedTest(name = "{displayName} for Encrypt: Java-V1-GCM, Decrypt: {0}") + @MethodSource("TestUtils#improvedClientsForTest") + public void GIVEN_GCMEncryptedData_AND_ImprovedClientDecryptingWithRequireEncryptRequireDecrypt_WHEN_Decrypt_THEN_Fail( + String language + ) { + // Given: decrypt language is an improved version + if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { + return; + } + + S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); + final String objectKey = "test-key-kms-v1-gcm-require-encrypt-require-decrypt-" + language; + final String input = "simple-test-input"; + KeyMaterial kmsKeyArn = KeyMaterial.builder() + .kmsKeyId(KMS_KEY_ARN) + .build(); + + // Create client with RequireEncryptRequireDecrypt commitment policy + CreateClientOutput output1 = client.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .enableLegacyWrappingAlgorithms(true) + .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT) + .build()) + .build()); + String s3ECId = output1.getClientId(); + + // Create the object using the old client with GCM encryption + // V1 Client with GCM + EncryptionMaterialsProvider materialsProvider = new KMSEncryptionMaterialsProvider(KMS_KEY_ARN); + + CryptoConfiguration v1Config = + new CryptoConfiguration(CryptoMode.StrictAuthenticatedEncryption) // StrictAuthenticatedEncryption uses GCM + .withStorageMode(CryptoStorageMode.ObjectMetadata) + .withAwsKmsRegion(KMS_REGION); + + AmazonS3Encryption v1Client = AmazonS3EncryptionClient.encryptionBuilder() + .withCryptoConfiguration(v1Config) + .withEncryptionMaterials(materialsProvider) + .build(); + + v1Client.putObject(BUCKET, objectKey, input); + + // When: decrypt GCM object with an improved version client with RequireEncryptRequireDecrypt policy + // Then: Fails + try { + client.getObject(GetObjectInput.builder() + .clientID(s3ECId) + .bucket(BUCKET) + .key(objectKey) + .build()); + fail("Expected Exception"); + } catch (S3EncryptionClientError e) { + assertTrue(e.getMessage().contains("TODO: Expected error message for decrypting with RequireEncryptRequireDecrypt policy")); + } + } + + // Exhaustive test 13 + // Outcome Version Operation Policy Content Encryption + // Pass Improved Decrypt RequireEncryptRequireDecrypt KC-GCM + + @ParameterizedTest(name = "{displayName} for Encrypt: {0}, Decrypt: {1}") + @MethodSource("TestUtils#crossLanguageClients") + public void GIVEN_KCGCMEncryptedData_AND_ImprovedClientDecryptingWithRequireEncryptRequireDecrypt_WHEN_Decrypt_THEN_Pass( + TestUtils.LanguageServerTarget encLang, TestUtils.LanguageServerTarget decLang + ) { + // Given: encrypt language is an improved version + if (!TestUtils.IMPROVED_VERSIONS.contains(encLang.getLanguageName())) { + return; + } + + // Given: decrypt language is an improved version + if (!TestUtils.IMPROVED_VERSIONS.contains(decLang.getLanguageName())) { + return; + } + + S3ECTestServerClient encClient = TestUtils.testServerClientFor(encLang); + final String objectKey = "encrypt-kc-gcm-decrypt-improved-require-encrypt-require-decrypt-" + encLang; + final String input = "simple-test-input"; + KeyMaterial kmsKeyArn = KeyMaterial.builder() + .kmsKeyId(KMS_KEY_ARN) + .build(); + CreateClientOutput encClientOutput = encClient.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT) + .build()) + .build()); + String encS3ECId = encClientOutput.getClientId(); + + // Given: object encrypted with key commitment + encClient.putObject(PutObjectInput.builder() + .clientID(encS3ECId) + .key(objectKey) + .bucket(BUCKET) + .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) + .build()); + + S3ECTestServerClient decClient = testServerClientFor(decLang); + // Create client with RequireEncryptRequireDecrypt commitment policy + CreateClientOutput decClientOutput = decClient.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT) + .build()) + .build()); + String decS3ECId = decClientOutput.getClientId(); + + // When: decrypt KC-GCM object with an improved version client with RequireEncryptRequireDecrypt policy + GetObjectOutput output = decClient.getObject(GetObjectInput.builder() + .clientID(decS3ECId) + .bucket(BUCKET) + .key(objectKey) + .build()); + + // Then: Pass + assertEquals(input, StandardCharsets.UTF_8.decode(output.getBody()).toString()); + } + + // Exhaustive test 14 + // Outcome Version Operation Policy Content Encryption + // Pass Improved Encrypt ForbidEncryptAllowDecrypt CBC + + @ParameterizedTest(name = "{displayName} for {0}") + @MethodSource("TestUtils#improvedClientsForTest") + public void GIVEN_ImprovedClientEncryptingWithForbidEncryptAllowDecrypt_WHEN_EncryptWithCBC_THEN_Pass( + String language + ) { + // Given: language is an improved version + if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { + return; + } + + S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); + final String objectKey = "encrypt-improved-forbid-encrypt-allow-decrypt-cbc-" + language; + final String input = "simple-test-input"; + KeyMaterial kmsKeyArn = KeyMaterial.builder() + .kmsKeyId(KMS_KEY_ARN) + .build(); + + // Create client with ForbidEncryptAllowDecrypt commitment policy + CreateClientOutput output1 = client.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .enableLegacyWrappingAlgorithms(true) + .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT) + .build()) + .build()); + String s3ECId = output1.getClientId(); + + // When: encrypt with CBC using an improved version client with ForbidEncryptAllowDecrypt policy + client.putObject(PutObjectInput.builder() + .clientID(s3ECId) + .key(objectKey) + .bucket(BUCKET) + .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) + .build()); + + // Then: Pass - verify we can decrypt the object + GetObjectOutput output = client.getObject(GetObjectInput.builder() + .clientID(s3ECId) + .bucket(BUCKET) + .key(objectKey) + .build()); + + assertEquals(input, new String(output.getBody().array())); + } + + // Exhaustive test 15 + // Outcome Version Operation Policy Content Encryption + // Pass Improved Encrypt ForbidEncryptAllowDecrypt GCM + + @ParameterizedTest(name = "{displayName} for {0}") + @MethodSource("TestUtils#improvedClientsForTest") + public void GIVEN_ImprovedClientEncryptingWithForbidEncryptAllowDecrypt_WHEN_EncryptWithGCM_THEN_Pass( + String language + ) { + // Given: language is an improved version + if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { + return; + } + + S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); + final String objectKey = "encrypt-improved-forbid-encrypt-allow-decrypt-gcm-" + language; + final String input = "simple-test-input"; + KeyMaterial kmsKeyArn = KeyMaterial.builder() + .kmsKeyId(KMS_KEY_ARN) + .build(); + + // Create client with ForbidEncryptAllowDecrypt commitment policy + CreateClientOutput output1 = client.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .enableLegacyWrappingAlgorithms(true) + .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT) + .build()) + .build()); + String s3ECId = output1.getClientId(); + + // When: encrypt with GCM using an improved version client with ForbidEncryptAllowDecrypt policy + client.putObject(PutObjectInput.builder() + .clientID(s3ECId) + .key(objectKey) + .bucket(BUCKET) + .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) + .build()); + + // Then: Pass - verify we can decrypt the object + GetObjectOutput output = client.getObject(GetObjectInput.builder() + .clientID(s3ECId) + .bucket(BUCKET) + .key(objectKey) + .build()); + + assertEquals(input, new String(output.getBody().array())); + } + + // Exhaustive test 16 + // Outcome Version Operation Policy Content Encryption + // Fail Improved Encrypt ForbidEncryptAllowDecrypt KC-GCM + + @ParameterizedTest(name = "{displayName} for {0}") + @MethodSource("TestUtils#improvedClientsForTest") + public void GIVEN_ImprovedClientEncryptingWithForbidEncryptAllowDecrypt_WHEN_EncryptWithKCGCM_THEN_Fail( + String language + ) { + // Given: language is an improved version + if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { + return; + } + + S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); + final String objectKey = "encrypt-improved-forbid-encrypt-allow-decrypt-kc-gcm-" + language; + final String input = "simple-test-input"; + KeyMaterial kmsKeyArn = KeyMaterial.builder() + .kmsKeyId(KMS_KEY_ARN) + .build(); + + // Create client with ForbidEncryptAllowDecrypt commitment policy + CreateClientOutput output1 = client.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT) + .build()) + .build()); + String s3ECId = output1.getClientId(); + + // When: attempt to encrypt with KC-GCM using an improved version client with ForbidEncryptAllowDecrypt policy + // Then: Fails + try { + client.putObject(PutObjectInput.builder() + .clientID(s3ECId) + .key(objectKey) + .bucket(BUCKET) + .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) + .build()); + fail("Expected Exception"); + } catch (S3EncryptionClientError e) { + assertTrue(e.getMessage().contains("TODO: Expected error message for encrypting with ForbidEncryptAllowDecrypt policy")); + } + } + + // Exhaustive test 17 + // Outcome Version Operation Policy Content Encryption + // Fail Improved Encrypt null CBC + + @ParameterizedTest(name = "{displayName} for {0}") + @MethodSource("TestUtils#improvedClientsForTest") + public void GIVEN_ImprovedClientEncryptingWithNullPolicy_WHEN_EncryptWithCBC_THEN_Fail( + String language + ) { + // Given: language is an improved version + if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { + return; + } + + S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); + final String objectKey = "encrypt-improved-null-policy-cbc-" + language; + final String input = "simple-test-input"; + KeyMaterial kmsKeyArn = KeyMaterial.builder() + .kmsKeyId(KMS_KEY_ARN) + .build(); + + // Create client with null commitment policy (not explicitly set) + CreateClientOutput output1 = client.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .enableLegacyWrappingAlgorithms(true) + .keyMaterial(kmsKeyArn) + // No commitment policy set - defaults to null + .build()) + .build()); + String s3ECId = output1.getClientId(); + + // When: attempt to encrypt with CBC using an improved version client with null policy + // Then: Fails + try { + client.putObject(PutObjectInput.builder() + .clientID(s3ECId) + .key(objectKey) + .bucket(BUCKET) + .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) + .build()); + fail("Expected Exception"); + } catch (S3EncryptionClientError e) { + assertTrue(e.getMessage().contains("TODO: Expected error message for encrypting with null policy")); + } + } + + // Exhaustive test 18 + // Outcome Version Operation Policy Content Encryption + // Fail Improved Encrypt null GCM + + @ParameterizedTest(name = "{displayName} for {0}") + @MethodSource("TestUtils#improvedClientsForTest") + public void GIVEN_ImprovedClientEncryptingWithNullPolicy_WHEN_EncryptWithGCM_THEN_Fail( + String language + ) { + // Given: language is an improved version + if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { + return; + } + + S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); + final String objectKey = "encrypt-improved-null-policy-gcm-" + language; + final String input = "simple-test-input"; + KeyMaterial kmsKeyArn = KeyMaterial.builder() + .kmsKeyId(KMS_KEY_ARN) + .build(); + + // Create client with null commitment policy (not explicitly set) + CreateClientOutput output1 = client.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .enableLegacyWrappingAlgorithms(true) + .keyMaterial(kmsKeyArn) + // No commitment policy set - defaults to null + .build()) + .build()); + String s3ECId = output1.getClientId(); + + // When: attempt to encrypt with GCM using an improved version client with null policy + // Then: Fails + try { + client.putObject(PutObjectInput.builder() + .clientID(s3ECId) + .key(objectKey) + .bucket(BUCKET) + .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) + .build()); + fail("Expected Exception"); + } catch (S3EncryptionClientError e) { + assertTrue(e.getMessage().contains("TODO: Expected error message for encrypting with null policy")); + } + } + + // Exhaustive test 19 + // Outcome Version Operation Policy Content Encryption + // Pass Improved Encrypt null KC-GCM + + @ParameterizedTest(name = "{displayName} for {0}") + @MethodSource("TestUtils#improvedClientsForTest") + public void GIVEN_ImprovedClientEncryptingWithNullPolicy_WHEN_EncryptWithKCGCM_THEN_Pass( + String language + ) { + // Given: language is an improved version + if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { + return; + } + + S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); + final String objectKey = "encrypt-improved-null-policy-kc-gcm-" + language; + final String input = "simple-test-input"; + KeyMaterial kmsKeyArn = KeyMaterial.builder() + .kmsKeyId(KMS_KEY_ARN) + .build(); + + // Create client with null commitment policy (not explicitly set) + CreateClientOutput output1 = client.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .keyMaterial(kmsKeyArn) + // No commitment policy set - defaults to null + .build()) + .build()); + String s3ECId = output1.getClientId(); + + // When: encrypt with KC-GCM using an improved version client with null policy + client.putObject(PutObjectInput.builder() + .clientID(s3ECId) + .key(objectKey) + .bucket(BUCKET) + .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) + .build()); + + // Then: Pass - verify we can decrypt the object + GetObjectOutput output = client.getObject(GetObjectInput.builder() + .clientID(s3ECId) + .bucket(BUCKET) + .key(objectKey) + .build()); + + assertEquals(input, new String(output.getBody().array())); + } + + // Exhaustive test 20 + // Outcome Version Operation Policy Content Encryption + // Fail Improved Encrypt RequireEncryptAllowDecrypt CBC + + @ParameterizedTest(name = "{displayName} for {0}") + @MethodSource("TestUtils#improvedClientsForTest") + public void GIVEN_ImprovedClientEncryptingWithRequireEncryptAllowDecrypt_WHEN_EncryptWithCBC_THEN_Fail( + String language + ) { + // Given: language is an improved version + if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { + return; + } + + S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); + final String objectKey = "encrypt-improved-require-encrypt-allow-decrypt-cbc-" + language; + final String input = "simple-test-input"; + KeyMaterial kmsKeyArn = KeyMaterial.builder() + .kmsKeyId(KMS_KEY_ARN) + .build(); + + // Create client with RequireEncryptAllowDecrypt commitment policy + CreateClientOutput output1 = client.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .enableLegacyWrappingAlgorithms(true) + .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT) + .build()) + .build()); + String s3ECId = output1.getClientId(); + + // When: attempt to encrypt with CBC using an improved version client with RequireEncryptAllowDecrypt policy + // Then: Fails + try { + client.putObject(PutObjectInput.builder() + .clientID(s3ECId) + .key(objectKey) + .bucket(BUCKET) + .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) + .build()); + fail("Expected Exception"); + } catch (S3EncryptionClientError e) { + assertTrue(e.getMessage().contains("TODO: Expected error message for encrypting with RequireEncryptAllowDecrypt policy")); + } + } + + // Exhaustive test 21 + // Outcome Version Operation Policy Content Encryption + // Fail Improved Encrypt RequireEncryptAllowDecrypt GCM + + @ParameterizedTest(name = "{displayName} for {0}") + @MethodSource("TestUtils#improvedClientsForTest") + public void GIVEN_ImprovedClientEncryptingWithRequireEncryptAllowDecrypt_WHEN_EncryptWithGCM_THEN_Fail( + String language + ) { + // Given: language is an improved version + if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { + return; + } + + S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); + final String objectKey = "encrypt-improved-require-encrypt-allow-decrypt-gcm-" + language; + final String input = "simple-test-input"; + KeyMaterial kmsKeyArn = KeyMaterial.builder() + .kmsKeyId(KMS_KEY_ARN) + .build(); + + // Create client with RequireEncryptAllowDecrypt commitment policy + CreateClientOutput output1 = client.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .enableLegacyWrappingAlgorithms(true) + .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT) + .build()) + .build()); + String s3ECId = output1.getClientId(); + + // When: attempt to encrypt with GCM using an improved version client with RequireEncryptAllowDecrypt policy + // Then: Fails + try { + client.putObject(PutObjectInput.builder() + .clientID(s3ECId) + .key(objectKey) + .bucket(BUCKET) + .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) + .build()); + fail("Expected Exception"); + } catch (S3EncryptionClientError e) { + assertTrue(e.getMessage().contains("TODO: Expected error message for encrypting with RequireEncryptAllowDecrypt policy")); + } + } + + // Exhaustive test 22 + // Outcome Version Operation Policy Content Encryption + // Pass Improved Encrypt RequireEncryptAllowDecrypt KC-GCM + + @ParameterizedTest(name = "{displayName} for {0}") + @MethodSource("TestUtils#improvedClientsForTest") + public void GIVEN_ImprovedClientEncryptingWithRequireEncryptAllowDecrypt_WHEN_EncryptWithKCGCM_THEN_Pass( + String language + ) { + // Given: language is an improved version + if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { + return; + } + + S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); + final String objectKey = "encrypt-improved-require-encrypt-allow-decrypt-kc-gcm-" + language; + final String input = "simple-test-input"; + KeyMaterial kmsKeyArn = KeyMaterial.builder() + .kmsKeyId(KMS_KEY_ARN) + .build(); + + // Create client with RequireEncryptAllowDecrypt commitment policy + CreateClientOutput output1 = client.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT) + .build()) + .build()); + String s3ECId = output1.getClientId(); + + // When: encrypt with KC-GCM using an improved version client with RequireEncryptAllowDecrypt policy + client.putObject(PutObjectInput.builder() + .clientID(s3ECId) + .key(objectKey) + .bucket(BUCKET) + .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) + .build()); + + // Then: Pass - verify we can decrypt the object + GetObjectOutput output = client.getObject(GetObjectInput.builder() + .clientID(s3ECId) + .bucket(BUCKET) + .key(objectKey) + .build()); + + assertEquals(input, new String(output.getBody().array())); + } + + // Exhaustive test 23 + // Outcome Version Operation Policy Content Encryption + // Fail Improved Encrypt RequireEncryptRequireDecrypt CBC + + @ParameterizedTest(name = "{displayName} for {0}") + @MethodSource("TestUtils#improvedClientsForTest") + public void GIVEN_ImprovedClientEncryptingWithRequireEncryptRequireDecrypt_WHEN_EncryptWithCBC_THEN_Fail( + String language + ) { + // Given: language is an improved version + if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { + return; + } + + S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); + final String objectKey = "encrypt-improved-require-encrypt-require-decrypt-cbc-" + language; + final String input = "simple-test-input"; + KeyMaterial kmsKeyArn = KeyMaterial.builder() + .kmsKeyId(KMS_KEY_ARN) + .build(); + + // Create client with RequireEncryptRequireDecrypt commitment policy + CreateClientOutput output1 = client.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .enableLegacyWrappingAlgorithms(true) + .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT) + .build()) + .build()); + String s3ECId = output1.getClientId(); + + // When: attempt to encrypt with CBC using an improved version client with RequireEncryptRequireDecrypt policy + // Then: Fails + try { + client.putObject(PutObjectInput.builder() + .clientID(s3ECId) + .key(objectKey) + .bucket(BUCKET) + .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) + .build()); + fail("Expected Exception"); + } catch (S3EncryptionClientError e) { + assertTrue(e.getMessage().contains("TODO: Expected error message for encrypting with RequireEncryptRequireDecrypt policy")); + } + } + + // Exhaustive test 24 + // Outcome Version Operation Policy Content Encryption + // Fail Improved Encrypt RequireEncryptRequireDecrypt GCM + + @ParameterizedTest(name = "{displayName} for {0}") + @MethodSource("TestUtils#improvedClientsForTest") + public void GIVEN_ImprovedClientEncryptingWithRequireEncryptRequireDecrypt_WHEN_EncryptWithGCM_THEN_Fail( + String language + ) { + // Given: language is an improved version + if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { + return; + } + + S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); + final String objectKey = "encrypt-improved-require-encrypt-require-decrypt-gcm-" + language; + final String input = "simple-test-input"; + KeyMaterial kmsKeyArn = KeyMaterial.builder() + .kmsKeyId(KMS_KEY_ARN) + .build(); + + // Create client with RequireEncryptRequireDecrypt commitment policy + CreateClientOutput output1 = client.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .enableLegacyWrappingAlgorithms(true) + .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT) + .build()) + .build()); + String s3ECId = output1.getClientId(); + + // When: attempt to encrypt with GCM using an improved version client with RequireEncryptRequireDecrypt policy + // Then: Fails + try { + client.putObject(PutObjectInput.builder() + .clientID(s3ECId) + .key(objectKey) + .bucket(BUCKET) + .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) + .build()); + fail("Expected Exception"); + } catch (S3EncryptionClientError e) { + assertTrue(e.getMessage().contains("TODO: Expected error message for encrypting with RequireEncryptRequireDecrypt policy")); + } + } +} diff --git a/test-server/model/client.smithy b/test-server/model/client.smithy index 4de56b5b..2175d98a 100644 --- a/test-server/model/client.smithy +++ b/test-server/model/client.smithy @@ -28,6 +28,12 @@ structure KeyMaterial { kmsKeyId: String } +enum CommitmentPolicy { + REQUIRE_ENCRYPT_REQUIRE_DECRYPT, + REQUIRE_ENCRYPT_ALLOW_DECRYPT, + FORBID_ENCRYPT_ALLOW_DECRYPT +} + structure S3ECConfig { enableLegacyUnauthenticatedModes: Boolean = false, enableDelayedAuthenticationMode: Boolean = false, diff --git a/test-server/model/object.smithy b/test-server/model/object.smithy index 623d8ed3..a4b12d5a 100644 --- a/test-server/model/object.smithy +++ b/test-server/model/object.smithy @@ -93,6 +93,39 @@ operation GetObject { } } +@readonly +@http(method: "GET", uri: "/object/{bucket}/{key}") +operation ReEncrypt { + input := for Object { + @httpLabel + @required + $bucket + + @httpLabel + @required + $key + + /// Should probably be renamed to be EC specific + @httpHeader("Content-Metadata") + $metadata + + @httpHeader("ClientID") + @required + @notProperty + clientID: String + } + + output := for Object { + @httpHeader("Content-Metadata") + @required + $metadata + + @required + @httpPayload + $body + } +} + /// Smithy does not know how to serialize a map list ObjectMetadata { member: String From a356802162025acb22eb3e4629bdb0a8a57bab51 Mon Sep 17 00:00:00 2001 From: Ryan Emery Date: Thu, 9 Oct 2025 12:06:18 -0700 Subject: [PATCH 3/6] Add some things --- test-server/java-tests/README.md | 4 +- .../amazon/encryption/s3/RoundTripTests.java | 42 +++++++++++++++---- .../amazon/encryption/s3/TestUtils.java | 26 ++++++------ test-server/model/client.smithy | 3 +- 4 files changed, 51 insertions(+), 24 deletions(-) diff --git a/test-server/java-tests/README.md b/test-server/java-tests/README.md index eee84863..2a9b80ee 100644 --- a/test-server/java-tests/README.md +++ b/test-server/java-tests/README.md @@ -1,8 +1,8 @@ -## Java Tests +# Java Tests This project contains Java client tests for the S3 Encryption Client. -### Running Tests +## Running Tests To run the integration tests for this project: diff --git a/test-server/java-tests/src/it/java/software/amazon/encryption/s3/RoundTripTests.java b/test-server/java-tests/src/it/java/software/amazon/encryption/s3/RoundTripTests.java index a94c561c..7c04f91f 100644 --- a/test-server/java-tests/src/it/java/software/amazon/encryption/s3/RoundTripTests.java +++ b/test-server/java-tests/src/it/java/software/amazon/encryption/s3/RoundTripTests.java @@ -23,6 +23,7 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import software.amazon.encryption.s3.client.S3ECTestServerClient; +import software.amazon.encryption.s3.model.CommitmentPolicy; import software.amazon.encryption.s3.model.CreateClientInput; import software.amazon.encryption.s3.model.CreateClientOutput; import software.amazon.encryption.s3.model.GetObjectInput; @@ -58,8 +59,12 @@ public void crossLanguageTestKms(LanguageServerTarget encLang, LanguageServerTar .kmsKeyId(KMS_KEY_ARN) .build(); CreateClientOutput encClientOutput = encClient.createClient(CreateClientInput.builder() - .config(S3ECConfig.builder() - .keyMaterial(kmsKeyArn).build()) + .config(S3ECConfig + .builder() + .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT) + .build() + ) .build()); String encS3ECId = encClientOutput.getClientId(); encClient.putObject(PutObjectInput.builder() @@ -71,7 +76,10 @@ public void crossLanguageTestKms(LanguageServerTarget encLang, LanguageServerTar S3ECTestServerClient decClient = testServerClientFor(decLang); CreateClientOutput decClientOutput = decClient.createClient(CreateClientInput.builder() .config(S3ECConfig.builder() - .keyMaterial(kmsKeyArn).build()) + .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT) + .build() + ) .build()); String decS3ECId = decClientOutput.getClientId(); GetObjectOutput output = decClient.getObject(GetObjectInput.builder() @@ -103,7 +111,10 @@ public void crossLanguageTestKmsWithEncCtx(LanguageServerTarget encLang, Languag .build(); CreateClientOutput encClientOutput = encClient.createClient(CreateClientInput.builder() .config(S3ECConfig.builder() - .keyMaterial(kmsKeyArn).build()) + .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT) + .build() + ) .build()); String encS3ECId = encClientOutput.getClientId(); @@ -117,7 +128,10 @@ public void crossLanguageTestKmsWithEncCtx(LanguageServerTarget encLang, Languag S3ECTestServerClient decClient = testServerClientFor(decLang); CreateClientOutput decClientOutput = decClient.createClient(CreateClientInput.builder() .config(S3ECConfig.builder() - .keyMaterial(kmsKeyArn).build()) + .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT) + .build() + ) .build()); String decS3ECId = decClientOutput.getClientId(); GetObjectOutput output = decClient.getObject(GetObjectInput.builder() @@ -167,7 +181,10 @@ public void crossLanguageTestKmsWithSubsetEncCtxFails(LanguageServerTarget encLa S3ECTestServerClient decClient = testServerClientFor(decLang); CreateClientOutput decClientOutput = decClient.createClient(CreateClientInput.builder() .config(S3ECConfig.builder() - .keyMaterial(kmsKeyArn).build()) + .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT) + .build() + ) .build()); String decS3ECId = decClientOutput.getClientId(); try { @@ -204,7 +221,10 @@ public void crossLanguageTestKmsWithIncorrectEncCtxFails(LanguageServerTarget en .build(); CreateClientOutput encClientOutput = encClient.createClient(CreateClientInput.builder() .config(S3ECConfig.builder() - .keyMaterial(kmsKeyArn).build()) + .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT) + .build() + ) .build()); String encS3ECId = encClientOutput.getClientId(); @@ -218,7 +238,10 @@ public void crossLanguageTestKmsWithIncorrectEncCtxFails(LanguageServerTarget en S3ECTestServerClient decClient = testServerClientFor(decLang); CreateClientOutput decClientOutput = decClient.createClient(CreateClientInput.builder() .config(S3ECConfig.builder() - .keyMaterial(kmsKeyArn).build()) + .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT) + .build() + ) .build()); String decS3ECId = decClientOutput.getClientId(); @@ -255,6 +278,7 @@ public void kmsV1Legacy(String language) { .config(S3ECConfig.builder() .enableLegacyWrappingAlgorithms(true) .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT) .build()) .build()); String s3ECId = output1.getClientId(); @@ -297,6 +321,7 @@ public void kmsV1LegacyWithEncCtx(String language) { .config(S3ECConfig.builder() .enableLegacyWrappingAlgorithms(true) .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT) .build()) .build()); String s3ECId = output1.getClientId(); @@ -346,6 +371,7 @@ public void kmsV1LegacyFailsWhenLegacyDisabled(String language) { .config(S3ECConfig.builder() .enableLegacyWrappingAlgorithms(false) .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT) .build()) .build()); String s3ECId = output1.getClientId(); diff --git a/test-server/java-tests/src/it/java/software/amazon/encryption/s3/TestUtils.java b/test-server/java-tests/src/it/java/software/amazon/encryption/s3/TestUtils.java index 4818b2a8..d90e7e90 100644 --- a/test-server/java-tests/src/it/java/software/amazon/encryption/s3/TestUtils.java +++ b/test-server/java-tests/src/it/java/software/amazon/encryption/s3/TestUtils.java @@ -92,23 +92,23 @@ public class TestUtils { public static final Set TRANSITION_VERSIONS = Set.of( - JAVA_V3_TRANSITION, - GO_V3_TRANSITION, - NET_V2_TRANSITION, - CPP_V2_TRANSITION, - RUBY_V2_TRANSITION, - PHP_V2_TRANSITION + // JAVA_V3_TRANSITION, + // GO_V3_TRANSITION, + // NET_V2_TRANSITION, + // CPP_V2_TRANSITION, + // PHP_V2_TRANSITION, + RUBY_V2_TRANSITION ); public static final Set IMPROVED_VERSIONS = Set.of( - JAVA_V4, - PYTHON_V3, - GO_V4, - NET_V3, - CPP_V3, - RUBY_V3, - PHP_V3 + // JAVA_V4, + // PYTHON_V3, + // GO_V4, + // NET_V3, + // CPP_V3, + // PHP_V3, + RUBY_V3 ); private static final Map serverMap; diff --git a/test-server/model/client.smithy b/test-server/model/client.smithy index 2175d98a..3f30c2d1 100644 --- a/test-server/model/client.smithy +++ b/test-server/model/client.smithy @@ -39,5 +39,6 @@ structure S3ECConfig { enableDelayedAuthenticationMode: Boolean = false, enableLegacyWrappingAlgorithms: Boolean = false, setBufferSize: Long, - keyMaterial: KeyMaterial + keyMaterial: KeyMaterial, + commitmentPolicy: CommitmentPolicy, } From c85acf50af3067c3afd216ef67ca3a3ddb4bea3a Mon Sep 17 00:00:00 2001 From: Ryan Emery Date: Thu, 9 Oct 2025 17:16:21 -0700 Subject: [PATCH 4/6] Getting Ruby v3 and tests passing --- .../s3/ExhaustiveRoundTripTests1_25.java | 1175 +---------------- .../amazon/encryption/s3/RoundTripTests.java | 22 +- .../amazon/encryption/s3/TestUtils.java | 24 +- test-server/ruby-v3-server/.duvet/config.toml | 5 + .../ruby-v3-server/lib/client_manager.rb | 22 +- test-server/ruby-v3-server/local-ruby-sdk | 2 +- 6 files changed, 78 insertions(+), 1172 deletions(-) diff --git a/test-server/java-tests/src/it/java/software/amazon/encryption/s3/ExhaustiveRoundTripTests1_25.java b/test-server/java-tests/src/it/java/software/amazon/encryption/s3/ExhaustiveRoundTripTests1_25.java index e1e7d098..eb48d6bd 100644 --- a/test-server/java-tests/src/it/java/software/amazon/encryption/s3/ExhaustiveRoundTripTests1_25.java +++ b/test-server/java-tests/src/it/java/software/amazon/encryption/s3/ExhaustiveRoundTripTests1_25.java @@ -8,31 +8,40 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; +import static software.amazon.encryption.s3.TestUtils.*; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Stream; import com.amazonaws.services.s3.model.KMSEncryptionMaterials; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import software.amazon.encryption.s3.model.*; import software.amazon.encryption.s3.client.S3ECTestServerClient; +import software.amazon.encryption.s3.model.CommitmentPolicy; +import software.amazon.encryption.s3.model.CreateClientInput; +import software.amazon.encryption.s3.model.CreateClientOutput; +import software.amazon.encryption.s3.model.GetObjectInput; +import software.amazon.encryption.s3.model.GetObjectOutput; +import software.amazon.encryption.s3.model.KeyMaterial; +import software.amazon.encryption.s3.model.PutObjectInput; +import software.amazon.encryption.s3.model.S3ECConfig; +import software.amazon.encryption.s3.model.S3EncryptionClientError; import com.amazonaws.services.s3.AmazonS3Encryption; import com.amazonaws.services.s3.AmazonS3EncryptionClient; import com.amazonaws.services.s3.model.CryptoConfiguration; import com.amazonaws.services.s3.model.CryptoMode; import com.amazonaws.services.s3.model.CryptoStorageMode; +import software.amazon.encryption.s3.TestUtils.*; import com.amazonaws.services.s3.model.EncryptionMaterialsProvider; import com.amazonaws.services.s3.model.KMSEncryptionMaterialsProvider; -import static software.amazon.encryption.s3.TestUtils.*; - /** * Exhaustive tests for S3 Encryption Client round-trip operations. * These tests cover various combinations of client versions, commitment policies, and encryption modes. @@ -46,92 +55,28 @@ public class ExhaustiveRoundTripTests1_25 { @BeforeAll public static void setup() { - TestUtils.setupTestServers(); + TestUtils.validateServersRunning(); } // Begin Exhaustive tests defined here: // https://tiny.amazon.com/3xnzwczl/loopcloumicrpeyJ3 - // Exhaustive test 1 - // Outcome Version Operation Policy Content Encryption - // Fail Current Decrypt null KC-GCM - - @ParameterizedTest(name = "{displayName} for Encrypt: {0}, Decrypt: {1}") - @MethodSource("TestUtils#crossLanguageClients") - public void GIVEN_DataEncryptedWithKC_AND_CurrentClientDecrypting_WHEN_Decrypt_THEN_Fail(TestUtils.LanguageServerTarget encLang, TestUtils.LanguageServerTarget decLang) { - // Given: encrypt language is either an improved version or a transition version - if (!TestUtils.IMPROVED_VERSIONS.contains(encLang.getLanguageName()) || !TestUtils.TRANSITION_VERSIONS.contains(encLang.getLanguageName())) { - return; - } - - // Given: decrypt language is a current version - if (!TestUtils.CURRENT_VERSIONS.contains(decLang.getLanguageName())) { - return; - } - - S3ECTestServerClient encClient = TestUtils.testServerClientFor(encLang); - final String objectKey = "encrypt-kc-decrypt-current-test-key-" + encLang; - final String input = "simple-test-input"; - KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(TestUtils.KMS_KEY_ARN) - .build(); - CreateClientOutput encClientOutput = encClient.createClient(CreateClientInput.builder() - .config(S3ECConfig.builder() - .keyMaterial(kmsKeyArn) - .commitmentPolicy(CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT) - .build()) - .build()); - String encS3ECId = encClientOutput.getClientId(); - // Given: object encrypted with key commitment - encClient.putObject(PutObjectInput.builder() - .clientID(encS3ECId) - .key(objectKey) - .bucket(TestUtils.BUCKET) - .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) - .build()); - S3ECTestServerClient decClient = TestUtils.testServerClientFor(decLang); - CreateClientOutput decClientOutput = decClient.createClient(CreateClientInput.builder() - .config(S3ECConfig.builder() - .keyMaterial(kmsKeyArn).build()) - .build()); - String decS3ECId = decClientOutput.getClientId(); - - // Then: Fails - try { - decClient.getObject(GetObjectInput.builder() - .clientID(decS3ECId) - .bucket(TestUtils.BUCKET) - .key(objectKey) - .build()); - fail("Expected Exception"); - } catch (S3EncryptionClientError e) { - assertTrue(e.getMessage().contains("TODO: Expected error message for decrypting unrecognized alg suite")); - } - } - // Exhaustive test 2 // Outcome Version Operation Policy Content Encryption // Pass Improved Decrypt ForbidEncryptAllowDecrypt CBC @ParameterizedTest(name = "{displayName} for Encrypt: Java-V1, Decrypt: {0}") - @MethodSource("TestUtils#improvedClientsForTest") + @MethodSource("software.amazon.encryption.s3.TestUtils#improvedClientsForTest") public void GIVEN_CBCEncryptedData_AND_ImprovedClientDecryptingWithForbidEncryptAllowDecrypt_WHEN_Decrypt_THEN_Pass( TestUtils.LanguageServerTarget language ) { - S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); + S3ECTestServerClient client = TestUtils.testServerClientFor(language); final String objectKey = "test-key-kms-v1-" + language; final String input = "simple-test-input"; KeyMaterial kmsKeyArn = KeyMaterial.builder() .kmsKeyId(TestUtils.KMS_KEY_ARN) .build(); - CreateClientOutput output1 = client.createClient(CreateClientInput.builder() - .config(S3ECConfig.builder() - .enableLegacyWrappingAlgorithms(true) - .keyMaterial(kmsKeyArn) - .build()) - .build()); - String s3ECId = output1.getClientId(); // Create the object using the old client // V1 Client @@ -152,7 +97,11 @@ public void GIVEN_CBCEncryptedData_AND_ImprovedClientDecryptingWithForbidEncrypt S3ECTestServerClient decClient = TestUtils.testServerClientFor(language); CreateClientOutput decClientOutput = decClient.createClient(CreateClientInput.builder() .config(S3ECConfig.builder() - .keyMaterial(kmsKeyArn).build()) + .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT) + .enableLegacyWrappingAlgorithms(true) + .build() + ) .build()); String decS3ECId = decClientOutput.getClientId(); @@ -164,11 +113,6 @@ public void GIVEN_CBCEncryptedData_AND_ImprovedClientDecryptingWithForbidEncrypt .build()); // Then: Pass - client.getObject(GetObjectInput.builder() - .clientID(s3ECId) - .bucket(TestUtils.BUCKET) - .key(objectKey) - .build()); } // Exhaustive test 3 @@ -176,11 +120,11 @@ public void GIVEN_CBCEncryptedData_AND_ImprovedClientDecryptingWithForbidEncrypt // Pass Improved Decrypt ForbidEncryptAllowDecrypt GCM @ParameterizedTest(name = "{displayName} for Encrypt: Java-V1-GCM, Decrypt: {0}") - @MethodSource("TestUtils#improvedClientsForTest") + @MethodSource("software.amazon.encryption.s3.TestUtils#improvedClientsForTest") public void GIVEN_GCMEncryptedData_AND_ImprovedClientDecryptingWithForbidEncryptAllowDecrypt_WHEN_Decrypt_THEN_Pass( - String language + TestUtils.LanguageServerTarget language ) { - S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); + S3ECTestServerClient client = TestUtils.testServerClientFor(language); final String objectKey = "test-key-kms-v1-gcm-" + language; final String input = "simple-test-input"; KeyMaterial kmsKeyArn = KeyMaterial.builder() @@ -227,19 +171,10 @@ public void GIVEN_GCMEncryptedData_AND_ImprovedClientDecryptingWithForbidEncrypt // Pass Improved Decrypt ForbidEncryptAllowDecrypt KC-GCM @ParameterizedTest(name = "{displayName} for Encrypt: {0}, Decrypt: {1}") - @MethodSource("TestUtils#crossLanguageClients") + @MethodSource("software.amazon.encryption.s3.TestUtils#encryptImprovedDecryptImproved") public void GIVEN_KCGCMEncryptedData_AND_ImprovedClientDecryptingWithForbidEncryptAllowDecrypt_WHEN_Decrypt_THEN_Pass( TestUtils.LanguageServerTarget encLang, TestUtils.LanguageServerTarget decLang ) { - // Given: encrypt language is an improved version or a transition version - if (!TestUtils.IMPROVED_VERSIONS.contains(encLang.getLanguageName()) || !TestUtils.TRANSITION_VERSIONS.contains(encLang.getLanguageName())) { - return; - } - - // Given: decrypt language is an improved version - if (!TestUtils.IMPROVED_VERSIONS.contains(decLang.getLanguageName())) { - return; - } S3ECTestServerClient encClient = TestUtils.testServerClientFor(encLang); final String objectKey = "encrypt-kc-gcm-decrypt-improved-test-key-" + encLang; @@ -283,1066 +218,4 @@ public void GIVEN_KCGCMEncryptedData_AND_ImprovedClientDecryptingWithForbidEncry assertEquals(input, StandardCharsets.UTF_8.decode(output.getBody()).toString()); } - - // Exhaustive test 5 - // Outcome Version Operation Policy Content Encryption - // Fail Improved Decrypt null CBC - - @ParameterizedTest(name = "{displayName} for Encrypt: Java-V1-CBC, Decrypt: {0}") - @MethodSource("TestUtils#improvedClientsForTest") - public void GIVEN_CBCEncryptedData_AND_ImprovedClientDecryptingWithNullPolicy_WHEN_Decrypt_THEN_Fail( - String language - ) { - // Given: decrypt language is an improved version - if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { - return; - } - - S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); - final String objectKey = "test-key-kms-v1-cbc-null-policy-" + language; - final String input = "simple-test-input"; - KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(TestUtils.KMS_KEY_ARN) - .build(); - - // Create client with null commitment policy (not explicitly set) - CreateClientOutput output1 = client.createClient(CreateClientInput.builder() - .config(S3ECConfig.builder() - .enableLegacyWrappingAlgorithms(true) - .keyMaterial(kmsKeyArn) - // No commitment policy set - defaults to null - .build()) - .build()); - String s3ECId = output1.getClientId(); - - // Create the object using the old client with CBC encryption - // V1 Client with CBC - EncryptionMaterialsProvider materialsProvider = new KMSEncryptionMaterialsProvider(TestUtils.KMS_KEY_ARN); - - CryptoConfiguration v1Config = - new CryptoConfiguration(CryptoMode.AuthenticatedEncryption) // AuthenticatedEncryption uses CBC - .withStorageMode(CryptoStorageMode.ObjectMetadata) - .withAwsKmsRegion(TestUtils.KMS_REGION); - - AmazonS3Encryption v1Client = AmazonS3EncryptionClient.encryptionBuilder() - .withCryptoConfiguration(v1Config) - .withEncryptionMaterials(materialsProvider) - .build(); - - v1Client.putObject(TestUtils.BUCKET, objectKey, input); - - // When: decrypt CBC object with an improved version client with null policy - // Then: Fails - try { - client.getObject(GetObjectInput.builder() - .clientID(s3ECId) - .bucket(TestUtils.BUCKET) - .key(objectKey) - .build()); - fail("Expected Exception"); - } catch (S3EncryptionClientError e) { - assertTrue(e.getMessage().contains("TODO: Expected error message for decrypting with null policy")); - } - } - - // Exhaustive test 6 - // Outcome Version Operation Policy Content Encryption - // Fail Improved Decrypt null GCM - - @ParameterizedTest(name = "{displayName} for Encrypt: Java-V1-GCM, Decrypt: {0}") - @MethodSource("TestUtils#improvedClientsForTest") - public void GIVEN_GCMEncryptedData_AND_ImprovedClientDecryptingWithNullPolicy_WHEN_Decrypt_THEN_Fail( - String language - ) { - // Given: decrypt language is an improved version - if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { - return; - } - - S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); - final String objectKey = "test-key-kms-v1-gcm-null-policy-" + language; - final String input = "simple-test-input"; - KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(TestUtils.KMS_KEY_ARN) - .build(); - - // Create client with null commitment policy (not explicitly set) - CreateClientOutput output1 = client.createClient(CreateClientInput.builder() - .config(S3ECConfig.builder() - .enableLegacyWrappingAlgorithms(true) - .keyMaterial(kmsKeyArn) - // No commitment policy set - defaults to null - .build()) - .build()); - String s3ECId = output1.getClientId(); - - // Create the object using the old client with GCM encryption - // V1 Client with GCM - EncryptionMaterialsProvider materialsProvider = new KMSEncryptionMaterialsProvider(TestUtils.KMS_KEY_ARN); - - CryptoConfiguration v1Config = - new CryptoConfiguration(CryptoMode.StrictAuthenticatedEncryption) // StrictAuthenticatedEncryption uses GCM - .withStorageMode(CryptoStorageMode.ObjectMetadata) - .withAwsKmsRegion(TestUtils.KMS_REGION); - - AmazonS3Encryption v1Client = AmazonS3EncryptionClient.encryptionBuilder() - .withCryptoConfiguration(v1Config) - .withEncryptionMaterials(materialsProvider) - .build(); - - v1Client.putObject(TestUtils.BUCKET, objectKey, input); - - // When: decrypt GCM object with an improved version client with null policy - // Then: Fails - try { - client.getObject(GetObjectInput.builder() - .clientID(s3ECId) - .bucket(TestUtils.BUCKET) - .key(objectKey) - .build()); - fail("Expected Exception"); - } catch (S3EncryptionClientError e) { - assertTrue(e.getMessage().contains("TODO: Expected error message for decrypting with null policy")); - } - } - - // Exhaustive test 7 - // Outcome Version Operation Policy Content Encryption - // Pass Improved Decrypt null KC-GCM - - @ParameterizedTest(name = "{displayName} for Encrypt: {0}, Decrypt: {1}") - @MethodSource("TestUtils#crossLanguageClients") - public void GIVEN_KCGCMEncryptedData_AND_ImprovedClientDecryptingWithNullPolicy_WHEN_Decrypt_THEN_Pass( - TestUtils.LanguageServerTarget encLang, TestUtils.LanguageServerTarget decLang - ) { - // Given: encrypt language is an improved version - if (!TestUtils.IMPROVED_VERSIONS.contains(encLang.getLanguageName())) { - return; - } - - // Given: decrypt language is an improved version - if (!TestUtils.IMPROVED_VERSIONS.contains(decLang.getLanguageName())) { - return; - } - - S3ECTestServerClient encClient = TestUtils.testServerClientFor(encLang); - final String objectKey = "encrypt-kc-gcm-decrypt-improved-null-policy-" + encLang; - final String input = "simple-test-input"; - KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(TestUtils.KMS_KEY_ARN) - .build(); - CreateClientOutput encClientOutput = encClient.createClient(CreateClientInput.builder() - .config(S3ECConfig.builder() - .keyMaterial(kmsKeyArn) - .commitmentPolicy(CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT) - .build()) - .build()); - String encS3ECId = encClientOutput.getClientId(); - - // Given: object encrypted with key commitment - encClient.putObject(PutObjectInput.builder() - .clientID(encS3ECId) - .key(objectKey) - .bucket(TestUtils.BUCKET) - .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) - .build()); - - S3ECTestServerClient decClient = TestUtils.testServerClientFor(decLang); - // Create client with null commitment policy (not explicitly set) - CreateClientOutput decClientOutput = decClient.createClient(CreateClientInput.builder() - .config(S3ECConfig.builder() - .keyMaterial(kmsKeyArn) - // No commitment policy set - defaults to null - .build()) - .build()); - String decS3ECId = decClientOutput.getClientId(); - - // When: decrypt KC-GCM object with an improved version client with null policy - GetObjectOutput output = decClient.getObject(GetObjectInput.builder() - .clientID(decS3ECId) - .bucket(TestUtils.BUCKET) - .key(objectKey) - .build()); - - // Then: Pass - assertEquals(input, StandardCharsets.UTF_8.decode(output.getBody()).toString()); - } - - - // Exhaustive test 8 - // Outcome Version Operation Policy Content Encryption - // Pass Improved Decrypt RequireEncryptAllowDecrypt CBC - - @ParameterizedTest(name = "{displayName} for Encrypt: Java-V1-CBC, Decrypt: {0}") - @MethodSource("TestUtils#improvedClientsForTest") - public void GIVEN_CBCEncryptedData_AND_ImprovedClientDecryptingWithRequireEncryptAllowDecrypt_WHEN_Decrypt_THEN_Pass( - String language - ) { - // Given: decrypt language is an improved version - if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { - return; - } - - S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); - final String objectKey = "test-key-kms-v1-cbc-require-encrypt-allow-decrypt-" + language; - final String input = "simple-test-input"; - KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(KMS_KEY_ARN) - .build(); - - // Create client with RequireEncryptAllowDecrypt commitment policy - CreateClientOutput output1 = client.createClient(CreateClientInput.builder() - .config(S3ECConfig.builder() - .enableLegacyWrappingAlgorithms(true) - .keyMaterial(kmsKeyArn) - .commitmentPolicy(CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT) - .build()) - .build()); - String s3ECId = output1.getClientId(); - - // Create the object using the old client with CBC encryption - // V1 Client with CBC - EncryptionMaterialsProvider materialsProvider = new KMSEncryptionMaterialsProvider(KMS_KEY_ARN); - - CryptoConfiguration v1Config = - new CryptoConfiguration(CryptoMode.AuthenticatedEncryption) // AuthenticatedEncryption uses CBC - .withStorageMode(CryptoStorageMode.ObjectMetadata) - .withAwsKmsRegion(KMS_REGION); - - AmazonS3Encryption v1Client = AmazonS3EncryptionClient.encryptionBuilder() - .withCryptoConfiguration(v1Config) - .withEncryptionMaterials(materialsProvider) - .build(); - - v1Client.putObject(BUCKET, objectKey, input); - - // When: decrypt CBC object with an improved version client with RequireEncryptAllowDecrypt policy - GetObjectOutput output = client.getObject(GetObjectInput.builder() - .clientID(s3ECId) - .bucket(BUCKET) - .key(objectKey) - .build()); - - // Then: Pass - assertEquals(input, new String(output.getBody().array())); - } - - // Exhaustive test 9 - // Outcome Version Operation Policy Content Encryption - // Pass Improved Decrypt RequireEncryptAllowDecrypt GCM - - @ParameterizedTest(name = "{displayName} for Encrypt: Java-V1-GCM, Decrypt: {0}") - @MethodSource("TestUtils#improvedClientsForTest") - public void GIVEN_GCMEncryptedData_AND_ImprovedClientDecryptingWithRequireEncryptAllowDecrypt_WHEN_Decrypt_THEN_Pass( - String language - ) { - // Given: decrypt language is an improved version - if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { - return; - } - - S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); - final String objectKey = "test-key-kms-v1-gcm-require-encrypt-allow-decrypt-" + language; - final String input = "simple-test-input"; - KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(KMS_KEY_ARN) - .build(); - - // Create client with RequireEncryptAllowDecrypt commitment policy - CreateClientOutput output1 = client.createClient(CreateClientInput.builder() - .config(S3ECConfig.builder() - .enableLegacyWrappingAlgorithms(true) - .keyMaterial(kmsKeyArn) - .commitmentPolicy(CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT) - .build()) - .build()); - String s3ECId = output1.getClientId(); - - // Create the object using the old client with GCM encryption - // V1 Client with GCM - EncryptionMaterialsProvider materialsProvider = new KMSEncryptionMaterialsProvider(KMS_KEY_ARN); - - CryptoConfiguration v1Config = - new CryptoConfiguration(CryptoMode.StrictAuthenticatedEncryption) // StrictAuthenticatedEncryption uses GCM - .withStorageMode(CryptoStorageMode.ObjectMetadata) - .withAwsKmsRegion(KMS_REGION); - - AmazonS3Encryption v1Client = AmazonS3EncryptionClient.encryptionBuilder() - .withCryptoConfiguration(v1Config) - .withEncryptionMaterials(materialsProvider) - .build(); - - v1Client.putObject(BUCKET, objectKey, input); - - // When: decrypt GCM object with an improved version client with RequireEncryptAllowDecrypt policy - GetObjectOutput output = client.getObject(GetObjectInput.builder() - .clientID(s3ECId) - .bucket(BUCKET) - .key(objectKey) - .build()); - - // Then: Pass - assertEquals(input, new String(output.getBody().array())); - } - - // Exhaustive test 10 - // Outcome Version Operation Policy Content Encryption - // Pass Improved Decrypt RequireEncryptAllowDecrypt KC-GCM - - @ParameterizedTest(name = "{displayName} for Encrypt: {0}, Decrypt: {1}") - @MethodSource("TestUtils#crossLanguageClients") - public void GIVEN_KCGCMEncryptedData_AND_ImprovedClientDecryptingWithRequireEncryptAllowDecrypt_WHEN_Decrypt_THEN_Pass( - TestUtils.LanguageServerTarget encLang, TestUtils.LanguageServerTarget decLang - ) { - // Given: encrypt language is an improved version - if (!TestUtils.IMPROVED_VERSIONS.contains(encLang.getLanguageName())) { - return; - } - - // Given: decrypt language is an improved version - if (!TestUtils.IMPROVED_VERSIONS.contains(decLang.getLanguageName())) { - return; - } - - S3ECTestServerClient encClient = TestUtils.testServerClientFor(encLang); - final String objectKey = "encrypt-kc-gcm-decrypt-improved-require-encrypt-allow-decrypt-" + encLang; - final String input = "simple-test-input"; - KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(KMS_KEY_ARN) - .build(); - CreateClientOutput encClientOutput = encClient.createClient(CreateClientInput.builder() - .config(S3ECConfig.builder() - .keyMaterial(kmsKeyArn) - .commitmentPolicy(CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT) - .build()) - .build()); - String encS3ECId = encClientOutput.getClientId(); - - // Given: object encrypted with key commitment - encClient.putObject(PutObjectInput.builder() - .clientID(encS3ECId) - .key(objectKey) - .bucket(BUCKET) - .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) - .build()); - - S3ECTestServerClient decClient = testServerClientFor(decLang); - // Create client with RequireEncryptAllowDecrypt commitment policy - CreateClientOutput decClientOutput = decClient.createClient(CreateClientInput.builder() - .config(S3ECConfig.builder() - .keyMaterial(kmsKeyArn) - .commitmentPolicy(CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT) - .build()) - .build()); - String decS3ECId = decClientOutput.getClientId(); - - // When: decrypt KC-GCM object with an improved version client with RequireEncryptAllowDecrypt policy - GetObjectOutput output = decClient.getObject(GetObjectInput.builder() - .clientID(decS3ECId) - .bucket(BUCKET) - .key(objectKey) - .build()); - - // Then: Pass - assertEquals(input, StandardCharsets.UTF_8.decode(output.getBody()).toString()); - } - - // Exhaustive test 11 - // Outcome Version Operation Policy Content Encryption - // Fail Improved Decrypt RequireEncryptRequireDecrypt CBC - - @ParameterizedTest(name = "{displayName} for Encrypt: Java-V1-CBC, Decrypt: {0}") - @MethodSource("TestUtils#improvedClientsForTest") - public void GIVEN_CBCEncryptedData_AND_ImprovedClientDecryptingWithRequireEncryptRequireDecrypt_WHEN_Decrypt_THEN_Fail( - String language - ) { - // Given: decrypt language is an improved version - if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { - return; - } - - S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); - final String objectKey = "test-key-kms-v1-cbc-require-encrypt-require-decrypt-" + language; - final String input = "simple-test-input"; - KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(KMS_KEY_ARN) - .build(); - - // Create client with RequireEncryptRequireDecrypt commitment policy - CreateClientOutput output1 = client.createClient(CreateClientInput.builder() - .config(S3ECConfig.builder() - .enableLegacyWrappingAlgorithms(true) - .keyMaterial(kmsKeyArn) - .commitmentPolicy(CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT) - .build()) - .build()); - String s3ECId = output1.getClientId(); - - // Create the object using the old client with CBC encryption - // V1 Client with CBC - EncryptionMaterialsProvider materialsProvider = new KMSEncryptionMaterialsProvider(KMS_KEY_ARN); - - CryptoConfiguration v1Config = - new CryptoConfiguration(CryptoMode.AuthenticatedEncryption) // AuthenticatedEncryption uses CBC - .withStorageMode(CryptoStorageMode.ObjectMetadata) - .withAwsKmsRegion(KMS_REGION); - - AmazonS3Encryption v1Client = AmazonS3EncryptionClient.encryptionBuilder() - .withCryptoConfiguration(v1Config) - .withEncryptionMaterials(materialsProvider) - .build(); - - v1Client.putObject(BUCKET, objectKey, input); - - // When: decrypt CBC object with an improved version client with RequireEncryptRequireDecrypt policy - // Then: Fails - try { - client.getObject(GetObjectInput.builder() - .clientID(s3ECId) - .bucket(BUCKET) - .key(objectKey) - .build()); - fail("Expected Exception"); - } catch (S3EncryptionClientError e) { - assertTrue(e.getMessage().contains("TODO: Expected error message for decrypting with RequireEncryptRequireDecrypt policy")); - } - } - - // Exhaustive test 12 - // Outcome Version Operation Policy Content Encryption - // Fail Improved Decrypt RequireEncryptRequireDecrypt GCM - - @ParameterizedTest(name = "{displayName} for Encrypt: Java-V1-GCM, Decrypt: {0}") - @MethodSource("TestUtils#improvedClientsForTest") - public void GIVEN_GCMEncryptedData_AND_ImprovedClientDecryptingWithRequireEncryptRequireDecrypt_WHEN_Decrypt_THEN_Fail( - String language - ) { - // Given: decrypt language is an improved version - if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { - return; - } - - S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); - final String objectKey = "test-key-kms-v1-gcm-require-encrypt-require-decrypt-" + language; - final String input = "simple-test-input"; - KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(KMS_KEY_ARN) - .build(); - - // Create client with RequireEncryptRequireDecrypt commitment policy - CreateClientOutput output1 = client.createClient(CreateClientInput.builder() - .config(S3ECConfig.builder() - .enableLegacyWrappingAlgorithms(true) - .keyMaterial(kmsKeyArn) - .commitmentPolicy(CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT) - .build()) - .build()); - String s3ECId = output1.getClientId(); - - // Create the object using the old client with GCM encryption - // V1 Client with GCM - EncryptionMaterialsProvider materialsProvider = new KMSEncryptionMaterialsProvider(KMS_KEY_ARN); - - CryptoConfiguration v1Config = - new CryptoConfiguration(CryptoMode.StrictAuthenticatedEncryption) // StrictAuthenticatedEncryption uses GCM - .withStorageMode(CryptoStorageMode.ObjectMetadata) - .withAwsKmsRegion(KMS_REGION); - - AmazonS3Encryption v1Client = AmazonS3EncryptionClient.encryptionBuilder() - .withCryptoConfiguration(v1Config) - .withEncryptionMaterials(materialsProvider) - .build(); - - v1Client.putObject(BUCKET, objectKey, input); - - // When: decrypt GCM object with an improved version client with RequireEncryptRequireDecrypt policy - // Then: Fails - try { - client.getObject(GetObjectInput.builder() - .clientID(s3ECId) - .bucket(BUCKET) - .key(objectKey) - .build()); - fail("Expected Exception"); - } catch (S3EncryptionClientError e) { - assertTrue(e.getMessage().contains("TODO: Expected error message for decrypting with RequireEncryptRequireDecrypt policy")); - } - } - - // Exhaustive test 13 - // Outcome Version Operation Policy Content Encryption - // Pass Improved Decrypt RequireEncryptRequireDecrypt KC-GCM - - @ParameterizedTest(name = "{displayName} for Encrypt: {0}, Decrypt: {1}") - @MethodSource("TestUtils#crossLanguageClients") - public void GIVEN_KCGCMEncryptedData_AND_ImprovedClientDecryptingWithRequireEncryptRequireDecrypt_WHEN_Decrypt_THEN_Pass( - TestUtils.LanguageServerTarget encLang, TestUtils.LanguageServerTarget decLang - ) { - // Given: encrypt language is an improved version - if (!TestUtils.IMPROVED_VERSIONS.contains(encLang.getLanguageName())) { - return; - } - - // Given: decrypt language is an improved version - if (!TestUtils.IMPROVED_VERSIONS.contains(decLang.getLanguageName())) { - return; - } - - S3ECTestServerClient encClient = TestUtils.testServerClientFor(encLang); - final String objectKey = "encrypt-kc-gcm-decrypt-improved-require-encrypt-require-decrypt-" + encLang; - final String input = "simple-test-input"; - KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(KMS_KEY_ARN) - .build(); - CreateClientOutput encClientOutput = encClient.createClient(CreateClientInput.builder() - .config(S3ECConfig.builder() - .keyMaterial(kmsKeyArn) - .commitmentPolicy(CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT) - .build()) - .build()); - String encS3ECId = encClientOutput.getClientId(); - - // Given: object encrypted with key commitment - encClient.putObject(PutObjectInput.builder() - .clientID(encS3ECId) - .key(objectKey) - .bucket(BUCKET) - .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) - .build()); - - S3ECTestServerClient decClient = testServerClientFor(decLang); - // Create client with RequireEncryptRequireDecrypt commitment policy - CreateClientOutput decClientOutput = decClient.createClient(CreateClientInput.builder() - .config(S3ECConfig.builder() - .keyMaterial(kmsKeyArn) - .commitmentPolicy(CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT) - .build()) - .build()); - String decS3ECId = decClientOutput.getClientId(); - - // When: decrypt KC-GCM object with an improved version client with RequireEncryptRequireDecrypt policy - GetObjectOutput output = decClient.getObject(GetObjectInput.builder() - .clientID(decS3ECId) - .bucket(BUCKET) - .key(objectKey) - .build()); - - // Then: Pass - assertEquals(input, StandardCharsets.UTF_8.decode(output.getBody()).toString()); - } - - // Exhaustive test 14 - // Outcome Version Operation Policy Content Encryption - // Pass Improved Encrypt ForbidEncryptAllowDecrypt CBC - - @ParameterizedTest(name = "{displayName} for {0}") - @MethodSource("TestUtils#improvedClientsForTest") - public void GIVEN_ImprovedClientEncryptingWithForbidEncryptAllowDecrypt_WHEN_EncryptWithCBC_THEN_Pass( - String language - ) { - // Given: language is an improved version - if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { - return; - } - - S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); - final String objectKey = "encrypt-improved-forbid-encrypt-allow-decrypt-cbc-" + language; - final String input = "simple-test-input"; - KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(KMS_KEY_ARN) - .build(); - - // Create client with ForbidEncryptAllowDecrypt commitment policy - CreateClientOutput output1 = client.createClient(CreateClientInput.builder() - .config(S3ECConfig.builder() - .enableLegacyWrappingAlgorithms(true) - .keyMaterial(kmsKeyArn) - .commitmentPolicy(CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT) - .build()) - .build()); - String s3ECId = output1.getClientId(); - - // When: encrypt with CBC using an improved version client with ForbidEncryptAllowDecrypt policy - client.putObject(PutObjectInput.builder() - .clientID(s3ECId) - .key(objectKey) - .bucket(BUCKET) - .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) - .build()); - - // Then: Pass - verify we can decrypt the object - GetObjectOutput output = client.getObject(GetObjectInput.builder() - .clientID(s3ECId) - .bucket(BUCKET) - .key(objectKey) - .build()); - - assertEquals(input, new String(output.getBody().array())); - } - - // Exhaustive test 15 - // Outcome Version Operation Policy Content Encryption - // Pass Improved Encrypt ForbidEncryptAllowDecrypt GCM - - @ParameterizedTest(name = "{displayName} for {0}") - @MethodSource("TestUtils#improvedClientsForTest") - public void GIVEN_ImprovedClientEncryptingWithForbidEncryptAllowDecrypt_WHEN_EncryptWithGCM_THEN_Pass( - String language - ) { - // Given: language is an improved version - if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { - return; - } - - S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); - final String objectKey = "encrypt-improved-forbid-encrypt-allow-decrypt-gcm-" + language; - final String input = "simple-test-input"; - KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(KMS_KEY_ARN) - .build(); - - // Create client with ForbidEncryptAllowDecrypt commitment policy - CreateClientOutput output1 = client.createClient(CreateClientInput.builder() - .config(S3ECConfig.builder() - .enableLegacyWrappingAlgorithms(true) - .keyMaterial(kmsKeyArn) - .commitmentPolicy(CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT) - .build()) - .build()); - String s3ECId = output1.getClientId(); - - // When: encrypt with GCM using an improved version client with ForbidEncryptAllowDecrypt policy - client.putObject(PutObjectInput.builder() - .clientID(s3ECId) - .key(objectKey) - .bucket(BUCKET) - .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) - .build()); - - // Then: Pass - verify we can decrypt the object - GetObjectOutput output = client.getObject(GetObjectInput.builder() - .clientID(s3ECId) - .bucket(BUCKET) - .key(objectKey) - .build()); - - assertEquals(input, new String(output.getBody().array())); - } - - // Exhaustive test 16 - // Outcome Version Operation Policy Content Encryption - // Fail Improved Encrypt ForbidEncryptAllowDecrypt KC-GCM - - @ParameterizedTest(name = "{displayName} for {0}") - @MethodSource("TestUtils#improvedClientsForTest") - public void GIVEN_ImprovedClientEncryptingWithForbidEncryptAllowDecrypt_WHEN_EncryptWithKCGCM_THEN_Fail( - String language - ) { - // Given: language is an improved version - if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { - return; - } - - S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); - final String objectKey = "encrypt-improved-forbid-encrypt-allow-decrypt-kc-gcm-" + language; - final String input = "simple-test-input"; - KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(KMS_KEY_ARN) - .build(); - - // Create client with ForbidEncryptAllowDecrypt commitment policy - CreateClientOutput output1 = client.createClient(CreateClientInput.builder() - .config(S3ECConfig.builder() - .keyMaterial(kmsKeyArn) - .commitmentPolicy(CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT) - .build()) - .build()); - String s3ECId = output1.getClientId(); - - // When: attempt to encrypt with KC-GCM using an improved version client with ForbidEncryptAllowDecrypt policy - // Then: Fails - try { - client.putObject(PutObjectInput.builder() - .clientID(s3ECId) - .key(objectKey) - .bucket(BUCKET) - .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) - .build()); - fail("Expected Exception"); - } catch (S3EncryptionClientError e) { - assertTrue(e.getMessage().contains("TODO: Expected error message for encrypting with ForbidEncryptAllowDecrypt policy")); - } - } - - // Exhaustive test 17 - // Outcome Version Operation Policy Content Encryption - // Fail Improved Encrypt null CBC - - @ParameterizedTest(name = "{displayName} for {0}") - @MethodSource("TestUtils#improvedClientsForTest") - public void GIVEN_ImprovedClientEncryptingWithNullPolicy_WHEN_EncryptWithCBC_THEN_Fail( - String language - ) { - // Given: language is an improved version - if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { - return; - } - - S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); - final String objectKey = "encrypt-improved-null-policy-cbc-" + language; - final String input = "simple-test-input"; - KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(KMS_KEY_ARN) - .build(); - - // Create client with null commitment policy (not explicitly set) - CreateClientOutput output1 = client.createClient(CreateClientInput.builder() - .config(S3ECConfig.builder() - .enableLegacyWrappingAlgorithms(true) - .keyMaterial(kmsKeyArn) - // No commitment policy set - defaults to null - .build()) - .build()); - String s3ECId = output1.getClientId(); - - // When: attempt to encrypt with CBC using an improved version client with null policy - // Then: Fails - try { - client.putObject(PutObjectInput.builder() - .clientID(s3ECId) - .key(objectKey) - .bucket(BUCKET) - .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) - .build()); - fail("Expected Exception"); - } catch (S3EncryptionClientError e) { - assertTrue(e.getMessage().contains("TODO: Expected error message for encrypting with null policy")); - } - } - - // Exhaustive test 18 - // Outcome Version Operation Policy Content Encryption - // Fail Improved Encrypt null GCM - - @ParameterizedTest(name = "{displayName} for {0}") - @MethodSource("TestUtils#improvedClientsForTest") - public void GIVEN_ImprovedClientEncryptingWithNullPolicy_WHEN_EncryptWithGCM_THEN_Fail( - String language - ) { - // Given: language is an improved version - if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { - return; - } - - S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); - final String objectKey = "encrypt-improved-null-policy-gcm-" + language; - final String input = "simple-test-input"; - KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(KMS_KEY_ARN) - .build(); - - // Create client with null commitment policy (not explicitly set) - CreateClientOutput output1 = client.createClient(CreateClientInput.builder() - .config(S3ECConfig.builder() - .enableLegacyWrappingAlgorithms(true) - .keyMaterial(kmsKeyArn) - // No commitment policy set - defaults to null - .build()) - .build()); - String s3ECId = output1.getClientId(); - - // When: attempt to encrypt with GCM using an improved version client with null policy - // Then: Fails - try { - client.putObject(PutObjectInput.builder() - .clientID(s3ECId) - .key(objectKey) - .bucket(BUCKET) - .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) - .build()); - fail("Expected Exception"); - } catch (S3EncryptionClientError e) { - assertTrue(e.getMessage().contains("TODO: Expected error message for encrypting with null policy")); - } - } - - // Exhaustive test 19 - // Outcome Version Operation Policy Content Encryption - // Pass Improved Encrypt null KC-GCM - - @ParameterizedTest(name = "{displayName} for {0}") - @MethodSource("TestUtils#improvedClientsForTest") - public void GIVEN_ImprovedClientEncryptingWithNullPolicy_WHEN_EncryptWithKCGCM_THEN_Pass( - String language - ) { - // Given: language is an improved version - if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { - return; - } - - S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); - final String objectKey = "encrypt-improved-null-policy-kc-gcm-" + language; - final String input = "simple-test-input"; - KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(KMS_KEY_ARN) - .build(); - - // Create client with null commitment policy (not explicitly set) - CreateClientOutput output1 = client.createClient(CreateClientInput.builder() - .config(S3ECConfig.builder() - .keyMaterial(kmsKeyArn) - // No commitment policy set - defaults to null - .build()) - .build()); - String s3ECId = output1.getClientId(); - - // When: encrypt with KC-GCM using an improved version client with null policy - client.putObject(PutObjectInput.builder() - .clientID(s3ECId) - .key(objectKey) - .bucket(BUCKET) - .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) - .build()); - - // Then: Pass - verify we can decrypt the object - GetObjectOutput output = client.getObject(GetObjectInput.builder() - .clientID(s3ECId) - .bucket(BUCKET) - .key(objectKey) - .build()); - - assertEquals(input, new String(output.getBody().array())); - } - - // Exhaustive test 20 - // Outcome Version Operation Policy Content Encryption - // Fail Improved Encrypt RequireEncryptAllowDecrypt CBC - - @ParameterizedTest(name = "{displayName} for {0}") - @MethodSource("TestUtils#improvedClientsForTest") - public void GIVEN_ImprovedClientEncryptingWithRequireEncryptAllowDecrypt_WHEN_EncryptWithCBC_THEN_Fail( - String language - ) { - // Given: language is an improved version - if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { - return; - } - - S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); - final String objectKey = "encrypt-improved-require-encrypt-allow-decrypt-cbc-" + language; - final String input = "simple-test-input"; - KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(KMS_KEY_ARN) - .build(); - - // Create client with RequireEncryptAllowDecrypt commitment policy - CreateClientOutput output1 = client.createClient(CreateClientInput.builder() - .config(S3ECConfig.builder() - .enableLegacyWrappingAlgorithms(true) - .keyMaterial(kmsKeyArn) - .commitmentPolicy(CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT) - .build()) - .build()); - String s3ECId = output1.getClientId(); - - // When: attempt to encrypt with CBC using an improved version client with RequireEncryptAllowDecrypt policy - // Then: Fails - try { - client.putObject(PutObjectInput.builder() - .clientID(s3ECId) - .key(objectKey) - .bucket(BUCKET) - .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) - .build()); - fail("Expected Exception"); - } catch (S3EncryptionClientError e) { - assertTrue(e.getMessage().contains("TODO: Expected error message for encrypting with RequireEncryptAllowDecrypt policy")); - } - } - - // Exhaustive test 21 - // Outcome Version Operation Policy Content Encryption - // Fail Improved Encrypt RequireEncryptAllowDecrypt GCM - - @ParameterizedTest(name = "{displayName} for {0}") - @MethodSource("TestUtils#improvedClientsForTest") - public void GIVEN_ImprovedClientEncryptingWithRequireEncryptAllowDecrypt_WHEN_EncryptWithGCM_THEN_Fail( - String language - ) { - // Given: language is an improved version - if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { - return; - } - - S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); - final String objectKey = "encrypt-improved-require-encrypt-allow-decrypt-gcm-" + language; - final String input = "simple-test-input"; - KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(KMS_KEY_ARN) - .build(); - - // Create client with RequireEncryptAllowDecrypt commitment policy - CreateClientOutput output1 = client.createClient(CreateClientInput.builder() - .config(S3ECConfig.builder() - .enableLegacyWrappingAlgorithms(true) - .keyMaterial(kmsKeyArn) - .commitmentPolicy(CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT) - .build()) - .build()); - String s3ECId = output1.getClientId(); - - // When: attempt to encrypt with GCM using an improved version client with RequireEncryptAllowDecrypt policy - // Then: Fails - try { - client.putObject(PutObjectInput.builder() - .clientID(s3ECId) - .key(objectKey) - .bucket(BUCKET) - .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) - .build()); - fail("Expected Exception"); - } catch (S3EncryptionClientError e) { - assertTrue(e.getMessage().contains("TODO: Expected error message for encrypting with RequireEncryptAllowDecrypt policy")); - } - } - - // Exhaustive test 22 - // Outcome Version Operation Policy Content Encryption - // Pass Improved Encrypt RequireEncryptAllowDecrypt KC-GCM - - @ParameterizedTest(name = "{displayName} for {0}") - @MethodSource("TestUtils#improvedClientsForTest") - public void GIVEN_ImprovedClientEncryptingWithRequireEncryptAllowDecrypt_WHEN_EncryptWithKCGCM_THEN_Pass( - String language - ) { - // Given: language is an improved version - if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { - return; - } - - S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); - final String objectKey = "encrypt-improved-require-encrypt-allow-decrypt-kc-gcm-" + language; - final String input = "simple-test-input"; - KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(KMS_KEY_ARN) - .build(); - - // Create client with RequireEncryptAllowDecrypt commitment policy - CreateClientOutput output1 = client.createClient(CreateClientInput.builder() - .config(S3ECConfig.builder() - .keyMaterial(kmsKeyArn) - .commitmentPolicy(CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT) - .build()) - .build()); - String s3ECId = output1.getClientId(); - - // When: encrypt with KC-GCM using an improved version client with RequireEncryptAllowDecrypt policy - client.putObject(PutObjectInput.builder() - .clientID(s3ECId) - .key(objectKey) - .bucket(BUCKET) - .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) - .build()); - - // Then: Pass - verify we can decrypt the object - GetObjectOutput output = client.getObject(GetObjectInput.builder() - .clientID(s3ECId) - .bucket(BUCKET) - .key(objectKey) - .build()); - - assertEquals(input, new String(output.getBody().array())); - } - - // Exhaustive test 23 - // Outcome Version Operation Policy Content Encryption - // Fail Improved Encrypt RequireEncryptRequireDecrypt CBC - - @ParameterizedTest(name = "{displayName} for {0}") - @MethodSource("TestUtils#improvedClientsForTest") - public void GIVEN_ImprovedClientEncryptingWithRequireEncryptRequireDecrypt_WHEN_EncryptWithCBC_THEN_Fail( - String language - ) { - // Given: language is an improved version - if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { - return; - } - - S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); - final String objectKey = "encrypt-improved-require-encrypt-require-decrypt-cbc-" + language; - final String input = "simple-test-input"; - KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(KMS_KEY_ARN) - .build(); - - // Create client with RequireEncryptRequireDecrypt commitment policy - CreateClientOutput output1 = client.createClient(CreateClientInput.builder() - .config(S3ECConfig.builder() - .enableLegacyWrappingAlgorithms(true) - .keyMaterial(kmsKeyArn) - .commitmentPolicy(CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT) - .build()) - .build()); - String s3ECId = output1.getClientId(); - - // When: attempt to encrypt with CBC using an improved version client with RequireEncryptRequireDecrypt policy - // Then: Fails - try { - client.putObject(PutObjectInput.builder() - .clientID(s3ECId) - .key(objectKey) - .bucket(BUCKET) - .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) - .build()); - fail("Expected Exception"); - } catch (S3EncryptionClientError e) { - assertTrue(e.getMessage().contains("TODO: Expected error message for encrypting with RequireEncryptRequireDecrypt policy")); - } - } - - // Exhaustive test 24 - // Outcome Version Operation Policy Content Encryption - // Fail Improved Encrypt RequireEncryptRequireDecrypt GCM - - @ParameterizedTest(name = "{displayName} for {0}") - @MethodSource("TestUtils#improvedClientsForTest") - public void GIVEN_ImprovedClientEncryptingWithRequireEncryptRequireDecrypt_WHEN_EncryptWithGCM_THEN_Fail( - String language - ) { - // Given: language is an improved version - if (!TestUtils.IMPROVED_VERSIONS.contains(language)) { - return; - } - - S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); - final String objectKey = "encrypt-improved-require-encrypt-require-decrypt-gcm-" + language; - final String input = "simple-test-input"; - KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(KMS_KEY_ARN) - .build(); - - // Create client with RequireEncryptRequireDecrypt commitment policy - CreateClientOutput output1 = client.createClient(CreateClientInput.builder() - .config(S3ECConfig.builder() - .enableLegacyWrappingAlgorithms(true) - .keyMaterial(kmsKeyArn) - .commitmentPolicy(CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT) - .build()) - .build()); - String s3ECId = output1.getClientId(); - - // When: attempt to encrypt with GCM using an improved version client with RequireEncryptRequireDecrypt policy - // Then: Fails - try { - client.putObject(PutObjectInput.builder() - .clientID(s3ECId) - .key(objectKey) - .bucket(BUCKET) - .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) - .build()); - fail("Expected Exception"); - } catch (S3EncryptionClientError e) { - assertTrue(e.getMessage().contains("TODO: Expected error message for encrypting with RequireEncryptRequireDecrypt policy")); - } - } } diff --git a/test-server/java-tests/src/it/java/software/amazon/encryption/s3/RoundTripTests.java b/test-server/java-tests/src/it/java/software/amazon/encryption/s3/RoundTripTests.java index 7c04f91f..a65117d7 100644 --- a/test-server/java-tests/src/it/java/software/amazon/encryption/s3/RoundTripTests.java +++ b/test-server/java-tests/src/it/java/software/amazon/encryption/s3/RoundTripTests.java @@ -267,8 +267,8 @@ public void crossLanguageTestKmsWithIncorrectEncCtxFails(LanguageServerTarget en @ParameterizedTest(name = "{displayName} for Encrypt: Java, Decrypt: {0}") @MethodSource("software.amazon.encryption.s3.TestUtils#clientsForTest") - public void kmsV1Legacy(String language) { - S3ECTestServerClient client = testServerClientFor(getServerMap().get(language)); + public void kmsV1Legacy(TestUtils.LanguageServerTarget language) { + S3ECTestServerClient client = testServerClientFor(language); final String objectKey = appendTestSuffix("test-key-kms-v1-" + language); final String input = "simple-test-input"; KeyMaterial kmsKeyArn = KeyMaterial.builder() @@ -310,8 +310,8 @@ public void kmsV1Legacy(String language) { @ParameterizedTest(name = "{displayName} for Encrypt: Java, Decrypt: {0}") @MethodSource("software.amazon.encryption.s3.TestUtils#clientsForTest") - public void kmsV1LegacyWithEncCtx(String language) { - S3ECTestServerClient client = testServerClientFor(getServerMap().get(language)); + public void kmsV1LegacyWithEncCtx(TestUtils.LanguageServerTarget language) { + S3ECTestServerClient client = testServerClientFor(language); final String objectKey = appendTestSuffix("test-key-kms-v1-with-enc-ctx-" + language); final String input = "simple-test-input"; KeyMaterial kmsKeyArn = KeyMaterial.builder() @@ -360,8 +360,8 @@ public void kmsV1LegacyWithEncCtx(String language) { @ParameterizedTest(name = "{displayName} for Encrypt: Java, Decrypt: {0}") @MethodSource("software.amazon.encryption.s3.TestUtils#clientsForTest") - public void kmsV1LegacyFailsWhenLegacyDisabled(String language) { - S3ECTestServerClient client = testServerClientFor(getServerMap().get(language)); + public void kmsV1LegacyFailsWhenLegacyDisabled(TestUtils.LanguageServerTarget language) { + S3ECTestServerClient client = testServerClientFor(language); final String objectKey = appendTestSuffix("test-key-kms-v1-fails-disabled" + language); final String input = "simple-test-input"; KeyMaterial kmsKeyArn = KeyMaterial.builder() @@ -400,13 +400,15 @@ public void kmsV1LegacyFailsWhenLegacyDisabled(String language) { .build()); fail("Expected Exception"); } catch (S3EncryptionClientError e) { - if (language.equals(NET_V3) || language.equals(NET_V2_CURRENT) - || language.equals(CPP_V2_CURRENT) || language.equals(CPP_V2_TRANSITION) || language.equals(CPP_V3)) { + if (language.getLanguageName().equals(NET_V3) || language.getLanguageName().equals(NET_V2_CURRENT) + || language.getLanguageName().equals(CPP_V2_CURRENT) || language.getLanguageName().equals(CPP_V2_TRANSITION) || language.getLanguageName().equals(CPP_V3)) { assertTrue(e.getMessage().contains( "The requested object is encrypted with V1 encryption schemas that have been disabled by client configuration" )); - } else if (language.equals(RUBY_V3) || language.equals(RUBY_V2_CURRENT)) { - assertTrue(e.getMessage().contains("The requested object is encrypted with V1 encryption schemas that have been disabled by client configuration security_profile = :v2. Retry with :v2_and_legacy or re-encrypt the object.")); + } else if (language.getLanguageName().equals(RUBY_V3) || language.getLanguageName().equals(RUBY_V2_CURRENT)) { + assertTrue(e.getMessage().contains( + "The requested object is encrypted with V1 encryption schemas that have been disabled by client configuration security_profile = :v2. Retry with :v2_and_legacy or re-encrypt the object." + ), "Actual error:" + e.getMessage()); } else { assertTrue(e.getMessage().contains("Enable legacy wrapping algorithms to use legacy key wrapping algorithm: kms")); } diff --git a/test-server/java-tests/src/it/java/software/amazon/encryption/s3/TestUtils.java b/test-server/java-tests/src/it/java/software/amazon/encryption/s3/TestUtils.java index d90e7e90..5d991a0c 100644 --- a/test-server/java-tests/src/it/java/software/amazon/encryption/s3/TestUtils.java +++ b/test-server/java-tests/src/it/java/software/amazon/encryption/s3/TestUtils.java @@ -279,7 +279,6 @@ public static void validateServersRunning() { */ public static Stream clientsForTest() { return serverMap.values().stream() - .map(LanguageServerTarget::getLanguageName) .map(Arguments::of); } @@ -287,24 +286,35 @@ public static Stream clientsForTest() { * Get stream of arguments for current version clients for testing. */ public static Stream currentClientsForTest() { - return clientsForTest() - .filter(arg -> CURRENT_VERSIONS.contains(arg.get()[0])); + return serverMap.values().stream() + .filter(target -> CURRENT_VERSIONS.contains(target.getLanguageName())) + .map(Arguments::of); } /** * Get stream of arguments for transition version clients for testing. */ public static Stream transitionClientsForTest() { - return clientsForTest() - .filter(arg -> TRANSITION_VERSIONS.contains(arg.get()[0])); + return serverMap.values().stream() + .filter(target -> TRANSITION_VERSIONS.contains(target.getLanguageName())) + .map(Arguments::of); } /** * Get stream of arguments for improved version clients for testing. */ public static Stream improvedClientsForTest() { - return clientsForTest() - .filter(arg -> IMPROVED_VERSIONS.contains(arg.get()[0])); + return serverMap.values().stream() + .filter(target -> IMPROVED_VERSIONS.contains(target.getLanguageName())) + .map(Arguments::of); + } + + public static Stream encryptImprovedDecryptImproved() { + return improvedClientsForTest() + .flatMap(t1 -> improvedClientsForTest() + .flatMap(t2 -> Stream.of( + Arguments.of(t1.get()[0], t2.get()[0]) + ))); } /** diff --git a/test-server/ruby-v3-server/.duvet/config.toml b/test-server/ruby-v3-server/.duvet/config.toml index 7118cd70..eaea972c 100644 --- a/test-server/ruby-v3-server/.duvet/config.toml +++ b/test-server/ruby-v3-server/.duvet/config.toml @@ -12,7 +12,12 @@ source = "../specification/s3-encryption/data-format/metadata-strategy.md" [[specification]] source = "../specification/s3-encryption/encryption.md" [[specification]] +source = "../specification/s3-encryption/decryption.md" +[[specification]] source = "../specification/s3-encryption/key-derivation.md" +[[specification]] +source = "../specification/s3-encryption/key-commitment.md" + [report.html] enabled = true diff --git a/test-server/ruby-v3-server/lib/client_manager.rb b/test-server/ruby-v3-server/lib/client_manager.rb index 261f19e6..d5d96801 100644 --- a/test-server/ruby-v3-server/lib/client_manager.rb +++ b/test-server/ruby-v3-server/lib/client_manager.rb @@ -25,10 +25,26 @@ def create_client(config) kms_key_id: kms_key_id, kms_client: @kms_client, key_wrap_schema: :kms_context, - content_encryption_schema: :aes_gcm_no_padding, + # content_encryption_schema: :aes_gcm_no_padding, # Set security profile based on legacy wrapping algorithms setting - security_profile: enable_legacy_wrapping ? :v2_and_legacy : :v2 - } + # security_profile: enable_legacy_wrapping ? :v2_and_legacy : :v2 + }.tap do |hash| + if !config['commitmentPolicy'].nil? + hash[:commitment_policy] = case config['commitmentPolicy'] + when 'FORBID_ENCRYPT_ALLOW_DECRYPT' + :forbid_encrypt_allow_decrypt + when 'REQUIRE_ENCRYPT_ALLOW_DECRYPT' + :require_encrypt_allow_decrypt + when 'REQUIRE_ENCRYPT_REQUIRE_DECRYPT' + :require_encrypt_require_decrypt + else + raise "Unsupported commitment_policy " + config['commitmentPolicy'] + end + end + if !config['enableLegacyWrappingAlgorithms'].nil? || !config['enableLegacyUnauthenticatedModes'].nil? + hash[:legacy_modes] = config['enableLegacyWrappingAlgorithms'] || config['enableLegacyUnauthenticatedModes'] + end + end # Create the S3 encryption client s3_client = Aws::S3::Client.new(region: 'us-west-2') diff --git a/test-server/ruby-v3-server/local-ruby-sdk b/test-server/ruby-v3-server/local-ruby-sdk index ba15842f..902f15e0 160000 --- a/test-server/ruby-v3-server/local-ruby-sdk +++ b/test-server/ruby-v3-server/local-ruby-sdk @@ -1 +1 @@ -Subproject commit ba15842f5b5d9cf6855a1023753d32eb8606bbef +Subproject commit 902f15e03d2816e11d1750f4279742ba35afaac5 From f0ab7319be1ae02f4bcb686f5e0217ea86f805f9 Mon Sep 17 00:00:00 2001 From: Ryan Emery Date: Thu, 9 Oct 2025 20:21:26 -0700 Subject: [PATCH 5/6] update test --- .gitignore | 1 + .../it/java/software/amazon/encryption/s3/RoundTripTests.java | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 0e29a9fb..9a2c0f8a 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ __pycache__/ # Distribution / packaging dist/ build/ +bin/ *.egg-info/ # Uv diff --git a/test-server/java-tests/src/it/java/software/amazon/encryption/s3/RoundTripTests.java b/test-server/java-tests/src/it/java/software/amazon/encryption/s3/RoundTripTests.java index a65117d7..f03a045c 100644 --- a/test-server/java-tests/src/it/java/software/amazon/encryption/s3/RoundTripTests.java +++ b/test-server/java-tests/src/it/java/software/amazon/encryption/s3/RoundTripTests.java @@ -167,7 +167,9 @@ public void crossLanguageTestKmsWithSubsetEncCtxFails(LanguageServerTarget encLa .build(); CreateClientOutput encClientOutput = encClient.createClient(CreateClientInput.builder() .config(S3ECConfig.builder() - .keyMaterial(kmsKeyArn).build()) + .keyMaterial(kmsKeyArn) + .commitmentPolicy(CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT) + .build()) .build()); String encS3ECId = encClientOutput.getClientId(); From 862fe576e257cf6673c23548826834f384232975 Mon Sep 17 00:00:00 2001 From: Ryan Emery Date: Thu, 9 Oct 2025 20:52:40 -0700 Subject: [PATCH 6/6] Add the various types --- .../amazon/encryption/s3/TestUtils.java | 58 ++++++++++++++++++- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/test-server/java-tests/src/it/java/software/amazon/encryption/s3/TestUtils.java b/test-server/java-tests/src/it/java/software/amazon/encryption/s3/TestUtils.java index 673acad3..ddb41fd1 100644 --- a/test-server/java-tests/src/it/java/software/amazon/encryption/s3/TestUtils.java +++ b/test-server/java-tests/src/it/java/software/amazon/encryption/s3/TestUtils.java @@ -310,11 +310,63 @@ public static Stream improvedClientsForTest() { .map(Arguments::of); } + /** + * These functions provide a stream of arguments for parameterized tests. + * @return Stream of Arguments containing pairs of LanguageServerTarget for encryption and decryption + */ public static Stream encryptImprovedDecryptImproved() { return improvedClientsForTest() - .flatMap(t1 -> improvedClientsForTest() - .flatMap(t2 -> Stream.of( - Arguments.of(t1.get()[0], t2.get()[0]) + .flatMap(encrypt -> improvedClientsForTest() + .flatMap(decrypt -> Stream.of( + Arguments.of(encrypt.get()[0], decrypt.get()[0]) + ))); + } + + public static Stream encryptImprovedDecryptTransition() { + return improvedClientsForTest() + .flatMap(encrypt -> transitionClientsForTest() + .flatMap(decrypt -> Stream.of( + Arguments.of(encrypt.get()[0], decrypt.get()[0]) + ))); + } + + public static Stream encryptTransitionDecryptImproved() { + return transitionClientsForTest() + .flatMap(encrypt -> improvedClientsForTest() + .flatMap(decrypt -> Stream.of( + Arguments.of(encrypt.get()[0], decrypt.get()[0]) + ))); + } + + public static Stream encryptImprovedDecryptCurrent() { + return improvedClientsForTest() + .flatMap(encrypt -> currentClientsForTest() + .flatMap(decrypt -> Stream.of( + Arguments.of(encrypt.get()[0], decrypt.get()[0]) + ))); + } + + public static Stream encryptCurrentDecryptImproved() { + return currentClientsForTest() + .flatMap(encrypt -> improvedClientsForTest() + .flatMap(decrypt -> Stream.of( + Arguments.of(encrypt.get()[0], decrypt.get()[0]) + ))); + } + + public static Stream encryptTransitionDecryptCurrent() { + return transitionClientsForTest() + .flatMap(encrypt -> currentClientsForTest() + .flatMap(decrypt -> Stream.of( + Arguments.of(encrypt.get()[0], decrypt.get()[0]) + ))); + } + + public static Stream encryptCurrentDecryptTransition() { + return currentClientsForTest() + .flatMap(encrypt -> transitionClientsForTest() + .flatMap(decrypt -> Stream.of( + Arguments.of(encrypt.get()[0], decrypt.get()[0]) ))); }