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 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 6c26368d..6e6cf9bd 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 @@ -105,6 +105,290 @@ 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 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 + 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", "bad length"); + + // 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#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 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 + 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 + 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", "bad length"); + + // 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#crossLanguageClients") public void crossLanguageTestKmsWithEncCtx(LanguageServerTarget encLang, LanguageServerTarget decLang) { @@ -672,4 +956,4 @@ public void instructionFileWriteAndReadWithRSA(LanguageServerTarget encLang, Lan assertEquals(input, new String(output.getBody().array())); } -} \ No newline at end of file +} 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..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,10 @@ 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 { error_log("This is the error: " . $e->getMessage()); return GenericServerError("Server error: " . $e->getMessage(), 500); diff --git a/test-server/php-v3-server/src/get_object.php b/test-server/php-v3-server/src/get_object.php index 3de7f779..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,10 @@ 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 { error_log("This is the error: " . $e->getMessage()); return GenericServerError("Server argument: " . $e->getMessage(), 500); 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