From 3ca423521d4ff6441bb07893d61880046a002f93 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 12 Nov 2025 13:08:33 -0800 Subject: [PATCH 01/13] m --- .../amazon/encryption/s3/RoundTripTests.java | 134 +++++++++++++++++- 1 file changed, 133 insertions(+), 1 deletion(-) 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 1167e4db..33d5da8b 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 @@ -104,6 +104,138 @@ public void crossLanguageTestKms(LanguageServerTarget encLang, LanguageServerTar } } + @ParameterizedTest(name = "{displayName} for Encrypt: {0}, Decrypt: {0}") + @MethodSource("software.amazon.encryption.s3.TestUtils#improvedClientsForTest") + public void improvedV3MessageDecryptionFailsWhenKcIncorrect(TestUtils.LanguageServerTarget language) { + S3ECTestServerClient client = testServerClientFor(language); + final String objectKey = appendTestSuffix("improved-v3-message-fails-when-kc-incorrect" + language); + final String input = "simple-test-input"; + KeyMaterial kmsKeyArn = KeyMaterial.builder() + .kmsKeyId(KMS_KEY_ARN) + .build(); + CreateClientOutput clientOutput = client.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .keyMaterial(kmsKeyArn) + .build()) + .build()); + String s3ECId = clientOutput.getClientId(); + + client.putObject(PutObjectInput.builder() + .clientID(s3ECId) + .key(objectKey) + .bucket(BUCKET) + .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) + .build()); + + // First, verify we can get the object successfully + GetObjectOutput output = client.getObject(GetObjectInput.builder() + .clientID(s3ECId) + .bucket(BUCKET) + .key(objectKey) + .build()); + assertEquals(input, new String(output.getBody().array())); + + // Create a plaintext S3 client to modify the object's metadata + try (S3Client ptS3Client = S3Client.create()) { + // Get the current object to preserve other metadata + ResponseBytes currentObject = ptS3Client.getObjectAsBytes(builder -> builder + .bucket(BUCKET) + .key(objectKey) + .build()); + + // Create new metadata with corrupted x-amz-d value + Map newMetadata = new HashMap<>(currentObject.response().metadata()); + newMetadata.put("x-amz-d", "HERE"); + + // Put the object back with corrupted metadata + ptS3Client.putObject(builder -> builder + .bucket(BUCKET) + .key(objectKey) + .metadata(newMetadata) + .build(), + software.amazon.awssdk.core.sync.RequestBody.fromBytes(currentObject.asByteArray())); + } + + // Now try to get the object again - this should fail due to corrupted metadata + try { + client.getObject(GetObjectInput.builder() + .clientID(s3ECId) + .bucket(BUCKET) + .key(objectKey) + .build()); + fail("Expected exception due to corrupted metadata!"); + } catch (S3EncryptionClientError e) { + // Expected - the decryption should fail due to corrupted key commitment metadata + // TODO: Messages + } + } + + @ParameterizedTest(name = "{displayName} for Encrypt: {0}, Decrypt: {0}") + @MethodSource("software.amazon.encryption.s3.TestUtils#transitionClientsForTest") + public void improvedV3MessageDecryptionFailsWhenKcIncorrect(TestUtils.LanguageServerTarget language) { + S3ECTestServerClient client = testServerClientFor(language); + final String objectKey = appendTestSuffix("improved-v3-message-fails-when-kc-incorrect" + language); + final String input = "simple-test-input"; + KeyMaterial kmsKeyArn = KeyMaterial.builder() + .kmsKeyId(KMS_KEY_ARN) + .build(); + CreateClientOutput clientOutput = client.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .keyMaterial(kmsKeyArn) + .build()) + .build()); + String s3ECId = clientOutput.getClientId(); + + client.putObject(PutObjectInput.builder() + .clientID(s3ECId) + .key(objectKey) + .bucket(BUCKET) + .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) + .build()); + + // First, verify we can get the object successfully + GetObjectOutput output = client.getObject(GetObjectInput.builder() + .clientID(s3ECId) + .bucket(BUCKET) + .key(objectKey) + .build()); + assertEquals(input, new String(output.getBody().array())); + + // Create a plaintext S3 client to modify the object's metadata + try (S3Client ptS3Client = S3Client.create()) { + // Get the current object to preserve other metadata + ResponseBytes currentObject = ptS3Client.getObjectAsBytes(builder -> builder + .bucket(BUCKET) + .key(objectKey) + .build()); + + // Create new metadata with corrupted x-amz-d value + Map newMetadata = new HashMap<>(currentObject.response().metadata()); + newMetadata.put("x-amz-d", "HERE"); + + // Put the object back with corrupted metadata + ptS3Client.putObject(builder -> builder + .bucket(BUCKET) + .key(objectKey) + .metadata(newMetadata) + .build(), + software.amazon.awssdk.core.sync.RequestBody.fromBytes(currentObject.asByteArray())); + } + + // Now try to get the object again - this should fail due to corrupted metadata + try { + client.getObject(GetObjectInput.builder() + .clientID(s3ECId) + .bucket(BUCKET) + .key(objectKey) + .build()); + fail("Expected exception due to corrupted metadata!"); + } catch (S3EncryptionClientError e) { + // Expected - the decryption should fail due to corrupted key commitment metadata + // TODO: Messages + } + } + @ParameterizedTest(name = "{displayName} for Encrypt: {0}, Decrypt: {1}") @MethodSource("software.amazon.encryption.s3.TestUtils#crossLanguageClients") public void crossLanguageTestKmsWithEncCtx(LanguageServerTarget encLang, LanguageServerTarget decLang) { @@ -609,4 +741,4 @@ public void instructionFileWriteAndRead(LanguageServerTarget encLang, LanguageSe assertEquals(input, new String(output.getBody().array())); } } -} \ No newline at end of file +} From 89acfcc9b2d03b7f8defaa8e58c133bc39c36cf6 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 12 Nov 2025 13:10:10 -0800 Subject: [PATCH 02/13] m --- .../it/java/software/amazon/encryption/s3/RoundTripTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 33d5da8b..30de8bf2 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 @@ -172,7 +172,7 @@ public void improvedV3MessageDecryptionFailsWhenKcIncorrect(TestUtils.LanguageSe @ParameterizedTest(name = "{displayName} for Encrypt: {0}, Decrypt: {0}") @MethodSource("software.amazon.encryption.s3.TestUtils#transitionClientsForTest") - public void improvedV3MessageDecryptionFailsWhenKcIncorrect(TestUtils.LanguageServerTarget language) { + public void transitionV3MessageDecryptionFailsWhenKcIncorrect(TestUtils.LanguageServerTarget language) { S3ECTestServerClient client = testServerClientFor(language); final String objectKey = appendTestSuffix("improved-v3-message-fails-when-kc-incorrect" + language); final String input = "simple-test-input"; From 3e40a01fd5865a843c3104c56721ee1d83e87788 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 12 Nov 2025 13:55:21 -0800 Subject: [PATCH 03/13] m --- .../go-v3-transition-server/local-go-s3ec | 2 +- test-server/go-v4-server/local-go-s3ec | 2 +- .../amazon/encryption/s3/RoundTripTests.java | 36 ++++++++++++------- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/test-server/go-v3-transition-server/local-go-s3ec b/test-server/go-v3-transition-server/local-go-s3ec index 7a29344c..00ba8b6b 160000 --- a/test-server/go-v3-transition-server/local-go-s3ec +++ b/test-server/go-v3-transition-server/local-go-s3ec @@ -1 +1 @@ -Subproject commit 7a29344cc0c431fd5ac6d0a08ce4db455d75c175 +Subproject commit 00ba8b6b304059cc34aea0a5ed3b875394489413 diff --git a/test-server/go-v4-server/local-go-s3ec b/test-server/go-v4-server/local-go-s3ec index 9946186d..00ba8b6b 160000 --- a/test-server/go-v4-server/local-go-s3ec +++ b/test-server/go-v4-server/local-go-s3ec @@ -1 +1 @@ -Subproject commit 9946186d6b760074750a535b663d6c84c5815308 +Subproject commit 00ba8b6b304059cc34aea0a5ed3b875394489413 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 30de8bf2..352eb28b 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 @@ -170,32 +170,42 @@ public void improvedV3MessageDecryptionFailsWhenKcIncorrect(TestUtils.LanguageSe } } - @ParameterizedTest(name = "{displayName} for Encrypt: {0}, Decrypt: {0}") - @MethodSource("software.amazon.encryption.s3.TestUtils#transitionClientsForTest") - public void transitionV3MessageDecryptionFailsWhenKcIncorrect(TestUtils.LanguageServerTarget language) { - S3ECTestServerClient client = testServerClientFor(language); - final String objectKey = appendTestSuffix("improved-v3-message-fails-when-kc-incorrect" + language); + @ParameterizedTest(name = "{displayName} for Encrypt: {0}, Decrypt: {1}") + @MethodSource("software.amazon.encryption.s3.TestUtils#encryptImprovedDecryptTransition") + public void transitionV3MessageDecryptionFailsWhenKcIncorrect( + TestUtils.LanguageServerTarget encLang, TestUtils.LanguageServerTarget decLang + ) { + S3ECTestServerClient encClient = testServerClientFor(encLang); + S3ECTestServerClient decClient = testServerClientFor(decLang); + final String objectKey = appendTestSuffix("transition-v3-message-fails-when-kc-incorrect" + encLang); final String input = "simple-test-input"; KeyMaterial kmsKeyArn = KeyMaterial.builder() .kmsKeyId(KMS_KEY_ARN) .build(); - CreateClientOutput clientOutput = client.createClient(CreateClientInput.builder() + CreateClientOutput encClientOutput = encClient.createClient(CreateClientInput.builder() .config(S3ECConfig.builder() .keyMaterial(kmsKeyArn) .build()) .build()); - String s3ECId = clientOutput.getClientId(); + String encS3ECId = encClientOutput.getClientId(); - client.putObject(PutObjectInput.builder() - .clientID(s3ECId) + encClient.putObject(PutObjectInput.builder() + .clientID(encS3ECId) .key(objectKey) .bucket(BUCKET) .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) .build()); + CreateClientOutput decClientOutput = decClient.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .keyMaterial(kmsKeyArn) + .build()) + .build()); + String decS3ECId = decClientOutput.getClientId(); + // First, verify we can get the object successfully - GetObjectOutput output = client.getObject(GetObjectInput.builder() - .clientID(s3ECId) + GetObjectOutput output = decClient.getObject(GetObjectInput.builder() + .clientID(decS3ECId) .bucket(BUCKET) .key(objectKey) .build()); @@ -224,8 +234,8 @@ public void transitionV3MessageDecryptionFailsWhenKcIncorrect(TestUtils.Language // Now try to get the object again - this should fail due to corrupted metadata try { - client.getObject(GetObjectInput.builder() - .clientID(s3ECId) + decClient.getObject(GetObjectInput.builder() + .clientID(decS3ECId) .bucket(BUCKET) .key(objectKey) .build()); From 641fc09ec7200c041f40b790b0aefb457d05e38a Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 12 Nov 2025 14:56:18 -0800 Subject: [PATCH 04/13] m --- test-server/go-v3-transition-server/local-go-s3ec | 2 +- test-server/php-v3-server/src/get_object.php | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/test-server/go-v3-transition-server/local-go-s3ec b/test-server/go-v3-transition-server/local-go-s3ec index 00ba8b6b..5f0cff03 160000 --- a/test-server/go-v3-transition-server/local-go-s3ec +++ b/test-server/go-v3-transition-server/local-go-s3ec @@ -1 +1 @@ -Subproject commit 00ba8b6b304059cc34aea0a5ed3b875394489413 +Subproject commit 5f0cff03956e8ee47d9833576afa66bd491246a2 diff --git a/test-server/php-v3-server/src/get_object.php b/test-server/php-v3-server/src/get_object.php index 3de7f779..8da98f9f 100644 --- a/test-server/php-v3-server/src/get_object.php +++ b/test-server/php-v3-server/src/get_object.php @@ -73,6 +73,8 @@ function handleGetObject($params) ob_end_clean(); } return GenericServerError("Invalid argument: " . $e->getMessage(), 400); + } catch (CryptoException $e) { + return S3EncryptionClientError("Crypto error: " . $e->getMessage()); } catch (Exception $e) { // Clean up output buffer if still active if (ob_get_level()) { From 9e7f9681aa1ba17622ed9ad13e22877bf2cd3d7f Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 12 Nov 2025 15:19:18 -0800 Subject: [PATCH 05/13] m --- test-server/go-v3-transition-server/local-go-s3ec | 2 +- test-server/go-v4-server/local-go-s3ec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test-server/go-v3-transition-server/local-go-s3ec b/test-server/go-v3-transition-server/local-go-s3ec index 5f0cff03..437212f9 160000 --- a/test-server/go-v3-transition-server/local-go-s3ec +++ b/test-server/go-v3-transition-server/local-go-s3ec @@ -1 +1 @@ -Subproject commit 5f0cff03956e8ee47d9833576afa66bd491246a2 +Subproject commit 437212f9b69fcb71374a3b3a2cda90c285137b5b diff --git a/test-server/go-v4-server/local-go-s3ec b/test-server/go-v4-server/local-go-s3ec index 00ba8b6b..437212f9 160000 --- a/test-server/go-v4-server/local-go-s3ec +++ b/test-server/go-v4-server/local-go-s3ec @@ -1 +1 @@ -Subproject commit 00ba8b6b304059cc34aea0a5ed3b875394489413 +Subproject commit 437212f9b69fcb71374a3b3a2cda90c285137b5b From a6f1543b6f6926c596dadb7354f1e15e8f6f5d24 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Wed, 12 Nov 2025 16:09:28 -0800 Subject: [PATCH 06/13] m --- test-server/php-v3-server/src/get_object.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test-server/php-v3-server/src/get_object.php b/test-server/php-v3-server/src/get_object.php index 8da98f9f..c9ed2424 100644 --- a/test-server/php-v3-server/src/get_object.php +++ b/test-server/php-v3-server/src/get_object.php @@ -73,8 +73,6 @@ function handleGetObject($params) ob_end_clean(); } return GenericServerError("Invalid argument: " . $e->getMessage(), 400); - } catch (CryptoException $e) { - return S3EncryptionClientError("Crypto error: " . $e->getMessage()); } catch (Exception $e) { // Clean up output buffer if still active if (ob_get_level()) { @@ -86,6 +84,8 @@ function handleGetObject($params) return S3EncryptionClientError($e->getMessage()); } elseif (strpos($e->getMessage(), "Message is encrypted with a non commiting algorithm but commitment policy is set to REQUIRE_ENCRYPT_REQUIRE_DECRYPT. Select a valid commitment policy to decrypt this object.") !== false) { return S3EncryptionClientError($e->getMessage()); + } elseif (strpos($e->getMessage(), "Calculated commitment key does not match expected commitment key value") !== false) { + return S3EncryptionClientError($e->getMessage()); } else { error_log("This is the error: " . $e->getMessage()); return GenericServerError("Server argument: " . $e->getMessage(), 500); From a993eb9884b0d76a34aa0ebcc964f456160fa96d Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Thu, 13 Nov 2025 10:31:16 -0800 Subject: [PATCH 07/13] m --- test-server/php-v2-transition-server/src/get_object.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test-server/php-v2-transition-server/src/get_object.php b/test-server/php-v2-transition-server/src/get_object.php index 5800e850..457ee467 100644 --- a/test-server/php-v2-transition-server/src/get_object.php +++ b/test-server/php-v2-transition-server/src/get_object.php @@ -80,6 +80,8 @@ function handleGetObject($params) } if (strpos($e->getMessage(), "@SecurityProfile=V2") !== false) { return S3EncryptionClientError($e->getMessage() . " " . "Enable legacy wrapping algorithms to use legacy key wrapping algorithm: kms"); + } elseif (strpos($e->getMessage(), "Calculated commitment key does not match expected commitment key value") !== false) { + return S3EncryptionClientError($e->getMessage()); } else { error_log("This is the error: " . $e->getMessage()); return GenericServerError("Server error: " . $e->getMessage(), 500); From 5059c89a60cf84e171b88e7d15ff0c7eec758dfd Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Thu, 13 Nov 2025 12:37:30 -0800 Subject: [PATCH 08/13] m --- test-server/php-v2-transition-server/src/get_object.php | 2 ++ test-server/php-v3-server/src/get_object.php | 2 ++ 2 files changed, 4 insertions(+) diff --git a/test-server/php-v2-transition-server/src/get_object.php b/test-server/php-v2-transition-server/src/get_object.php index 457ee467..1f9f9324 100644 --- a/test-server/php-v2-transition-server/src/get_object.php +++ b/test-server/php-v2-transition-server/src/get_object.php @@ -80,6 +80,8 @@ function handleGetObject($params) } if (strpos($e->getMessage(), "@SecurityProfile=V2") !== false) { return S3EncryptionClientError($e->getMessage() . " " . "Enable legacy wrapping algorithms to use legacy key wrapping algorithm: kms"); + } elseif (strpos($e->getMessage(), "Invalid Commitment Key length found in object envelope.") !== false) { + return S3EncryptionClientError($e->getMessage()); } elseif (strpos($e->getMessage(), "Calculated commitment key does not match expected commitment key value") !== false) { return S3EncryptionClientError($e->getMessage()); } else { diff --git a/test-server/php-v3-server/src/get_object.php b/test-server/php-v3-server/src/get_object.php index c9ed2424..9ae2802e 100644 --- a/test-server/php-v3-server/src/get_object.php +++ b/test-server/php-v3-server/src/get_object.php @@ -84,6 +84,8 @@ function handleGetObject($params) return S3EncryptionClientError($e->getMessage()); } elseif (strpos($e->getMessage(), "Message is encrypted with a non commiting algorithm but commitment policy is set to REQUIRE_ENCRYPT_REQUIRE_DECRYPT. Select a valid commitment policy to decrypt this object.") !== false) { return S3EncryptionClientError($e->getMessage()); + } elseif (strpos($e->getMessage(), "Invalid Commitment Key length found in object envelope.") !== false) { + return S3EncryptionClientError($e->getMessage()); } elseif (strpos($e->getMessage(), "Calculated commitment key does not match expected commitment key value") !== false) { return S3EncryptionClientError($e->getMessage()); } else { From 2c4a8320c0b726c7b28410f62a0b1f6b5e59e317 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Thu, 13 Nov 2025 12:43:45 -0800 Subject: [PATCH 09/13] m --- .../amazon/encryption/s3/RoundTripTests.java | 146 +++++++++++++++++- 1 file changed, 144 insertions(+), 2 deletions(-) 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 352eb28b..7e51e567 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 @@ -135,6 +135,72 @@ public void improvedV3MessageDecryptionFailsWhenKcIncorrect(TestUtils.LanguageSe .build()); assertEquals(input, new String(output.getBody().array())); + // Create a plaintext S3 client to modify the object's metadata + try (S3Client ptS3Client = S3Client.create()) { + // Get the current object to preserve other metadata + ResponseBytes currentObject = ptS3Client.getObjectAsBytes(builder -> builder + .bucket(BUCKET) + .key(objectKey) + .build()); + + // Create new metadata with incorrect x-amz-d value + Map newMetadata = new HashMap<>(currentObject.response().metadata()); + newMetadata.put("x-amz-d", "WFWqRjRc5BhBplPYIcvApAC2SYybUZw9T+TR+RmO//8="); + + // Put the object back with corrupted metadata + ptS3Client.putObject(builder -> builder + .bucket(BUCKET) + .key(objectKey) + .metadata(newMetadata) + .build(), + software.amazon.awssdk.core.sync.RequestBody.fromBytes(currentObject.asByteArray())); + } + + // Now try to get the object again - this should fail due to corrupted metadata + try { + client.getObject(GetObjectInput.builder() + .clientID(s3ECId) + .bucket(BUCKET) + .key(objectKey) + .build()); + fail("Expected exception due to corrupted metadata!"); + } catch (S3EncryptionClientError e) { + // Expected - the decryption should fail due to corrupted key commitment metadata + // TODO: Messages + } + } + + @ParameterizedTest(name = "{displayName} for Encrypt: {0}, Decrypt: {0}") + @MethodSource("software.amazon.encryption.s3.TestUtils#improvedClientsForTest") + public void improvedV3MessageDecryptionFailsWhenKcIncorrectLength(TestUtils.LanguageServerTarget language) { + S3ECTestServerClient client = testServerClientFor(language); + final String objectKey = appendTestSuffix("improved-v3-message-fails-when-kc-incorrect" + language); + final String input = "simple-test-input"; + KeyMaterial kmsKeyArn = KeyMaterial.builder() + .kmsKeyId(KMS_KEY_ARN) + .build(); + CreateClientOutput clientOutput = client.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .keyMaterial(kmsKeyArn) + .build()) + .build()); + String s3ECId = clientOutput.getClientId(); + + client.putObject(PutObjectInput.builder() + .clientID(s3ECId) + .key(objectKey) + .bucket(BUCKET) + .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) + .build()); + + // First, verify we can get the object successfully + GetObjectOutput output = client.getObject(GetObjectInput.builder() + .clientID(s3ECId) + .bucket(BUCKET) + .key(objectKey) + .build()); + assertEquals(input, new String(output.getBody().array())); + // Create a plaintext S3 client to modify the object's metadata try (S3Client ptS3Client = S3Client.create()) { // Get the current object to preserve other metadata @@ -145,7 +211,7 @@ public void improvedV3MessageDecryptionFailsWhenKcIncorrect(TestUtils.LanguageSe // Create new metadata with corrupted x-amz-d value Map newMetadata = new HashMap<>(currentObject.response().metadata()); - newMetadata.put("x-amz-d", "HERE"); + newMetadata.put("x-amz-d", "bad length"); // Put the object back with corrupted metadata ptS3Client.putObject(builder -> builder @@ -211,6 +277,82 @@ public void transitionV3MessageDecryptionFailsWhenKcIncorrect( .build()); assertEquals(input, new String(output.getBody().array())); + // Create a plaintext S3 client to modify the object's metadata + try (S3Client ptS3Client = S3Client.create()) { + // Get the current object to preserve other metadata + ResponseBytes currentObject = ptS3Client.getObjectAsBytes(builder -> builder + .bucket(BUCKET) + .key(objectKey) + .build()); + + // Create new metadata with incorrect x-amz-d value + Map newMetadata = new HashMap<>(currentObject.response().metadata()); + newMetadata.put("x-amz-d", "WFWqRjRc5BhBplPYIcvApAC2SYybUZw9T+TR+RmO//8="); + + // Put the object back with corrupted metadata + ptS3Client.putObject(builder -> builder + .bucket(BUCKET) + .key(objectKey) + .metadata(newMetadata) + .build(), + software.amazon.awssdk.core.sync.RequestBody.fromBytes(currentObject.asByteArray())); + } + + // Now try to get the object again - this should fail due to corrupted metadata + try { + decClient.getObject(GetObjectInput.builder() + .clientID(decS3ECId) + .bucket(BUCKET) + .key(objectKey) + .build()); + fail("Expected exception due to corrupted metadata!"); + } catch (S3EncryptionClientError e) { + // Expected - the decryption should fail due to corrupted key commitment metadata + // TODO: Messages + } + } + + @ParameterizedTest(name = "{displayName} for Encrypt: {0}, Decrypt: {1}") + @MethodSource("software.amazon.encryption.s3.TestUtils#encryptImprovedDecryptTransition") + public void transitionV3MessageDecryptionFailsWhenKcIncorrectLength( + TestUtils.LanguageServerTarget encLang, TestUtils.LanguageServerTarget decLang + ) { + S3ECTestServerClient encClient = testServerClientFor(encLang); + S3ECTestServerClient decClient = testServerClientFor(decLang); + final String objectKey = appendTestSuffix("transition-v3-message-fails-when-kc-incorrect" + 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) + .build()) + .build()); + String encS3ECId = encClientOutput.getClientId(); + + encClient.putObject(PutObjectInput.builder() + .clientID(encS3ECId) + .key(objectKey) + .bucket(BUCKET) + .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) + .build()); + + CreateClientOutput decClientOutput = decClient.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .keyMaterial(kmsKeyArn) + .build()) + .build()); + String decS3ECId = decClientOutput.getClientId(); + + // First, verify we can get the object successfully + GetObjectOutput output = decClient.getObject(GetObjectInput.builder() + .clientID(decS3ECId) + .bucket(BUCKET) + .key(objectKey) + .build()); + assertEquals(input, new String(output.getBody().array())); + // Create a plaintext S3 client to modify the object's metadata try (S3Client ptS3Client = S3Client.create()) { // Get the current object to preserve other metadata @@ -221,7 +363,7 @@ public void transitionV3MessageDecryptionFailsWhenKcIncorrect( // Create new metadata with corrupted x-amz-d value Map newMetadata = new HashMap<>(currentObject.response().metadata()); - newMetadata.put("x-amz-d", "HERE"); + newMetadata.put("x-amz-d", "bad length"); // Put the object back with corrupted metadata ptS3Client.putObject(builder -> builder From 9b9b11798ba03e5100130193f2ee72fffd70e7c4 Mon Sep 17 00:00:00 2001 From: Ryan Emery Date: Thu, 13 Nov 2025 15:26:51 -0800 Subject: [PATCH 10/13] update ruby Use the right namespace in Ruby And map the error corectly in the test server --- test-server/ruby-v2-server/app.rb | 2 +- test-server/ruby-v2-server/local-ruby-sdk | 2 +- test-server/ruby-v3-server/local-ruby-sdk | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test-server/ruby-v2-server/app.rb b/test-server/ruby-v2-server/app.rb index cde757a3..7b1f9c19 100644 --- a/test-server/ruby-v2-server/app.rb +++ b/test-server/ruby-v2-server/app.rb @@ -201,7 +201,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-v2-server/local-ruby-sdk b/test-server/ruby-v2-server/local-ruby-sdk index 582e0241..c7afe162 160000 --- a/test-server/ruby-v2-server/local-ruby-sdk +++ b/test-server/ruby-v2-server/local-ruby-sdk @@ -1 +1 @@ -Subproject commit 582e02418ac2c5540c48dd089a7db506712e6f94 +Subproject commit c7afe1622906808ce3b57c40c7ebd8b3906c183a diff --git a/test-server/ruby-v3-server/local-ruby-sdk b/test-server/ruby-v3-server/local-ruby-sdk index 582e0241..c7afe162 160000 --- a/test-server/ruby-v3-server/local-ruby-sdk +++ b/test-server/ruby-v3-server/local-ruby-sdk @@ -1 +1 @@ -Subproject commit 582e02418ac2c5540c48dd089a7db506712e6f94 +Subproject commit c7afe1622906808ce3b57c40c7ebd8b3906c183a From ef4a49a111229a42830660376db39db0ff9798f2 Mon Sep 17 00:00:00 2001 From: Ryan Emery Date: Thu, 13 Nov 2025 15:27:13 -0800 Subject: [PATCH 11/13] simpfily things --- test-server/Makefile | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/test-server/Makefile b/test-server/Makefile index 9b18b857..bd8d9ef6 100644 --- a/test-server/Makefile +++ b/test-server/Makefile @@ -2,9 +2,6 @@ .PHONY: all start-servers run-tests stop-servers clean ci check-env help -# Default target -all: start-all-servers wait-all-servers run-tests - # CI target for GitHub Actions ci: $(MAKE) build-all-servers @@ -19,9 +16,10 @@ BUILD_SERVER_TARGETS := $(addprefix build-, $(SERVER_DIRS)) START_SERVER_TARGETS := $(addprefix start-, $(SERVER_DIRS)) WAIT_SERVER_TARGETS := $(addprefix wait-, $(SERVER_DIRS)) -# Build all servers in parallel -build-all-servers: export MAKEFLAGS=-j$(shell sysctl -n hw.ncpu 2>/dev/null || nproc 2>/dev/null || echo 1) -build-all-servers: $(BUILD_SERVER_TARGETS) +build-all-servers: + @echo "Building all servers..." + @$(MAKE) $(BUILD_SERVER_TARGETS) + @echo "All servers built" $(BUILD_SERVER_TARGETS): build-%: @if [ -f $*/Makefile ]; then \ @@ -32,21 +30,11 @@ $(BUILD_SERVER_TARGETS): build-%: exit 1; \ fi -# Build and start all servers -start-servers: - @echo "Building all servers..." - $(MAKE) build-all-servers - @echo "Starting all servers..." - $(MAKE) start-all-servers - @echo "Waiting for servers to start..." - @for dir in $(SERVER_DIRS); do \ - echo "Waiting for server in $$dir..."; \ - $(MAKE) -C $$dir wait-for-server; \ - done - # Start servers sequentially (no parallel execution) start-all-servers: - @$(MAKE) MAKEFLAGS= $(START_SERVER_TARGETS) + @echo "Starting all server processes..." + @$(MAKE) $(START_SERVER_TARGETS) + @echo "All servers processes started!" $(START_SERVER_TARGETS): start-%: @if [ -f $*/Makefile ]; then \ From d1a1151c0f60969863fe0d913ae2eac5f5e2b727 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Fri, 14 Nov 2025 10:12:24 -0800 Subject: [PATCH 12/13] m --- test-server/Makefile | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/test-server/Makefile b/test-server/Makefile index bd8d9ef6..9b18b857 100644 --- a/test-server/Makefile +++ b/test-server/Makefile @@ -2,6 +2,9 @@ .PHONY: all start-servers run-tests stop-servers clean ci check-env help +# Default target +all: start-all-servers wait-all-servers run-tests + # CI target for GitHub Actions ci: $(MAKE) build-all-servers @@ -16,10 +19,9 @@ BUILD_SERVER_TARGETS := $(addprefix build-, $(SERVER_DIRS)) START_SERVER_TARGETS := $(addprefix start-, $(SERVER_DIRS)) WAIT_SERVER_TARGETS := $(addprefix wait-, $(SERVER_DIRS)) -build-all-servers: - @echo "Building all servers..." - @$(MAKE) $(BUILD_SERVER_TARGETS) - @echo "All servers built" +# Build all servers in parallel +build-all-servers: export MAKEFLAGS=-j$(shell sysctl -n hw.ncpu 2>/dev/null || nproc 2>/dev/null || echo 1) +build-all-servers: $(BUILD_SERVER_TARGETS) $(BUILD_SERVER_TARGETS): build-%: @if [ -f $*/Makefile ]; then \ @@ -30,11 +32,21 @@ $(BUILD_SERVER_TARGETS): build-%: exit 1; \ fi +# Build and start all servers +start-servers: + @echo "Building all servers..." + $(MAKE) build-all-servers + @echo "Starting all servers..." + $(MAKE) start-all-servers + @echo "Waiting for servers to start..." + @for dir in $(SERVER_DIRS); do \ + echo "Waiting for server in $$dir..."; \ + $(MAKE) -C $$dir wait-for-server; \ + done + # Start servers sequentially (no parallel execution) start-all-servers: - @echo "Starting all server processes..." - @$(MAKE) $(START_SERVER_TARGETS) - @echo "All servers processes started!" + @$(MAKE) MAKEFLAGS= $(START_SERVER_TARGETS) $(START_SERVER_TARGETS): start-%: @if [ -f $*/Makefile ]; then \ From dfa19aba820669d8621ecda63e3eedcf707903f1 Mon Sep 17 00:00:00 2001 From: Andy Jewell Date: Mon, 17 Nov 2025 11:47:48 -0500 Subject: [PATCH 13/13] bump cpp sdk --- test-server/cpp-v2-server/aws-sdk-cpp | 2 +- test-server/cpp-v3-server/aws-sdk-cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test-server/cpp-v2-server/aws-sdk-cpp b/test-server/cpp-v2-server/aws-sdk-cpp index 994384ca..5276e9ec 160000 --- a/test-server/cpp-v2-server/aws-sdk-cpp +++ b/test-server/cpp-v2-server/aws-sdk-cpp @@ -1 +1 @@ -Subproject commit 994384ca8b9defe2ae60b5d3447ec5f47f7ec19f +Subproject commit 5276e9ec0fbe6d296c5941ae6bbf6d401063c607 diff --git a/test-server/cpp-v3-server/aws-sdk-cpp b/test-server/cpp-v3-server/aws-sdk-cpp index 52eeeddd..87402c99 160000 --- a/test-server/cpp-v3-server/aws-sdk-cpp +++ b/test-server/cpp-v3-server/aws-sdk-cpp @@ -1 +1 @@ -Subproject commit 52eeeddd8c40c1547832781f2e48478afff6a6ad +Subproject commit 87402c99fd3c9107c6ccc6edf545fd4b05b2b551