From b8413d6a5aaf70c790fb8fa4a87f244f740c21d0 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Thu, 25 Sep 2025 10:47:56 -0700 Subject: [PATCH 1/4] refactor --- .../amazon/encryption/s3/RoundTripTests.java | 306 ++++-------------- .../amazon/encryption/s3/TestUtils.java | 235 ++++++++++++++ 2 files changed, 295 insertions(+), 246 deletions(-) create mode 100644 test-server/java-tests/src/it/java/software/amazon/encryption/s3/TestUtils.java 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 cd67f2e0..b2160eb1 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 @@ -9,19 +9,11 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; -import java.net.Socket; -import java.net.URI; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; import java.util.stream.Stream; import com.amazonaws.services.s3.model.KMSEncryptionMaterials; @@ -29,10 +21,6 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import software.amazon.smithy.java.aws.client.restjson.RestJsonClientProtocol; -import software.amazon.smithy.java.client.core.ClientConfig; -import software.amazon.smithy.java.client.core.ClientProtocol; -import software.amazon.smithy.java.client.core.endpoint.EndpointResolver; import software.amazon.encryption.s3.client.S3ECTestServerClient; import software.amazon.encryption.s3.model.CreateClientInput; import software.amazon.encryption.s3.model.CreateClientOutput; @@ -41,206 +29,32 @@ 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.S3ECTestServerApiService; import software.amazon.encryption.s3.model.S3EncryptionClientError; -import software.amazon.smithy.java.http.api.HttpRequest; -import software.amazon.smithy.java.http.api.HttpResponse; -import com.amazonaws.regions.Region; -import com.amazonaws.regions.Regions; 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.LanguageServerTarget; import com.amazonaws.services.s3.model.EncryptionMaterialsProvider; import com.amazonaws.services.s3.model.KMSEncryptionMaterialsProvider; public class RoundTripTests { - private static final String JAVA_V3 = "Java-V3"; - private static final String PYTHON_V3 = "Python-V3"; - private static final String GO_V3 = "Go-V3"; - private static final String CPP_V2 = "CPP-V2"; - private static final String NET_V2 = "NET-V2"; - private static final String NET_V3 = "NET-V3"; - private static final String PHP_V2 = "PHP-V2"; - private static final String PHP_V3 = "PHP-V3"; - private static final String RUBY_V2 = "Ruby-V2"; - private static final String RUBY_V3 = "Ruby-V3"; - - private static final Map serverMap; - - private static final String KMS_KEY_ARN = System.getenv("TEST_SERVER_KMS_KEY_ARN") != null ? - System.getenv("TEST_SERVER_KMS_KEY_ARN") : "arn:aws:kms:us-west-2:370957321024:alias/S3EC-Test-Server-Github-KMS-Key"; - private static final Region KMS_REGION = Region.getRegion(Regions.fromName("us-west-2")); - private static final String BUCKET = System.getenv("TEST_SERVER_S3_BUCKET") != null ? - System.getenv("TEST_SERVER_S3_BUCKET") : "s3ec-test-server-github-bucket"; - - static { - final Map servers = new LinkedHashMap<>(); - servers.put(JAVA_V3, new LanguageServerTarget(JAVA_V3, "8080")); - servers.put(PYTHON_V3, new LanguageServerTarget(PYTHON_V3, "8081")); - servers.put(GO_V3, new LanguageServerTarget(GO_V3, "8082")); - servers.put(NET_V2, new LanguageServerTarget(NET_V2, "8083")); - servers.put(NET_V3, new LanguageServerTarget(NET_V3, "8084")); - servers.put(CPP_V2, new LanguageServerTarget(CPP_V2, "8085")); - servers.put(PHP_V2, new LanguageServerTarget(PHP_V2, "8087")); - servers.put(PHP_V3, new LanguageServerTarget(PHP_V3, "8093")); - servers.put(RUBY_V2, new LanguageServerTarget(RUBY_V2, "8086")); - servers.put(RUBY_V3, new LanguageServerTarget(RUBY_V3, "8092")); - - serverMap = filterServers(servers); - } - - private static Map filterServers(Map allServers) { - - final String maybeFilter = System.getProperty("test.filter.servers"); - if (maybeFilter == null || maybeFilter.trim().isEmpty()) { - return allServers; // No filtering - use all servers - } - - final String[] filters = Arrays.stream(maybeFilter.split(",")) - .map(String::trim) - .map(String::toLowerCase) - .toArray(String[]::new); - - return allServers.entrySet().stream() - .filter(entry -> { - String key = entry.getKey().toLowerCase(); - return Arrays.stream(filters).anyMatch(key::contains); - }) - .collect(Collectors.toMap( - Map.Entry::getKey, - Map.Entry::getValue, - (e1, e2) -> e1, // merge function (not really needed) - LinkedHashMap::new // preserve order - )); - } - - // Encryption context validation behavior varies by implementation: - // - Go: Does not validate encryption context on decrypt operations - // - .NET: Only validates against encryption context stored in the object metadata - // If the encryption context provided to getObject does not match the encryption context on the stored object, - // these implementations will not raise an error as expected. - // For now, skip tests that expect encryption context validation on decrypt. - private static final Set ENCRYPTION_CONTEXT_ON_DECRYPT_UNSUPPORTED = - Set.of(GO_V3, PHP_V2, PHP_V3, NET_V2, NET_V3); - - // S3EC .NET implementations does not accept encryption context (EC) during putObject operations. - // These tests are not configured to pass encryption context at client level but at encrypt, - // So, for .NET EC is not passed. - // For now, skip tests that expect encryption context validation on decrypt. - private static final Set ENCRYPTION_CONTEXT_ON_ENCRYPT_UNSUPPORTED = - Set.of(NET_V2, NET_V3); - - static public class LanguageServerTarget { - public String getLanguageName() { - return languageName; - } - - public URI getServerURI() { - return serverURI; - } - - private final String baseURI = "http://localhost"; - private String languageName; - private URI serverURI; - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - LanguageServerTarget that = (LanguageServerTarget) o; - return Objects.equals(languageName, that.languageName) && Objects.equals(serverURI, that.serverURI); - } - - @Override - public int hashCode() { - return Objects.hash(languageName, serverURI); - } - - LanguageServerTarget(String language, String port) { - languageName = language; - serverURI = URI.create(baseURI+ ":" + port); - } - - @Override - public String toString() { - return languageName; - } - } @BeforeAll public static void setup() { - // Wait for servers to start - for (LanguageServerTarget server : serverMap.values()) { - if (!serverListening(server.getServerURI())) { - throw new RuntimeException(String.format("Test Server for %s is not running at endpoint: %s", server.getLanguageName(), server.getServerURI())); - } - } - } - - public static boolean serverListening(URI uri) { - try (Socket ignored = new Socket(uri.getHost(), uri.getPort())) { - return true; - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - - static S3ECTestServerClient testServerClientFor(LanguageServerTarget server) { - S3ECTestServerApiService apiService = S3ECTestServerApiService.instance(); - ClientProtocol rest = new RestJsonClientProtocol(apiService.schema().id()); - return S3ECTestServerClient.builder() - .endpointResolver(EndpointResolver.staticEndpoint(server.serverURI)) - .withConfiguration(ClientConfig.builder() - .service(apiService) - .protocol(rest) - .endpointResolver(EndpointResolver.staticEndpoint(server.serverURI)) - .build()) - .build(); - } - - static Stream clientsForTest() { - return serverMap.values().stream() - .map(LanguageServerTarget::getLanguageName) - .map(Arguments::of); - } - - static Stream crossLanguageClients() { - return serverMap.values().stream() - .flatMap(t1 -> serverMap.values().stream() - .flatMap(t2 -> Stream.of( - Arguments.of(t1, t2) - ))); - } - - /** - * Annoyingly, Smithy doesn't provide an interface for map types - * in HTTP headers, so we have to do the serde ourselves - * Servers need an equivalent utility. - * TODO: Move to a utilities class or something. - */ - private List metadataMapToList(Map md) { - List mdAsList = new ArrayList<>(md.size()); - for (Map.Entry keyValue : md.entrySet()) { - // Using ":" because Smithy will parse "," into a flattened list - mdAsList.add("[" + keyValue.getKey() + "]:[" + keyValue.getValue() + "]"); - } - return mdAsList; + TestUtils.validateServersRunning(); } @ParameterizedTest(name = "{displayName} for Encrypt: {0}, Decrypt: {1}") - @MethodSource("crossLanguageClients") + @MethodSource("software.amazon.encryption.s3.TestUtils#crossLanguageClients") public void crossLanguageTestKms(LanguageServerTarget encLang, LanguageServerTarget decLang) { - S3ECTestServerClient encClient = testServerClientFor(encLang); + S3ECTestServerClient encClient = TestUtils.testServerClientFor(encLang); final String objectKey = "cross-lang-test-key-" + encLang; final String input = "simple-test-input"; KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(KMS_KEY_ARN) + .kmsKeyId(TestUtils.KMS_KEY_ARN) .build(); CreateClientOutput encClientOutput = encClient.createClient(CreateClientInput.builder() .config(S3ECConfig.builder() @@ -250,10 +64,10 @@ public void crossLanguageTestKms(LanguageServerTarget encLang, LanguageServerTar encClient.putObject(PutObjectInput.builder() .clientID(encS3ECId) .key(objectKey) - .bucket(BUCKET) + .bucket(TestUtils.BUCKET) .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) .build()); - S3ECTestServerClient decClient = testServerClientFor(decLang); + S3ECTestServerClient decClient = TestUtils.testServerClientFor(decLang); CreateClientOutput decClientOutput = decClient.createClient(CreateClientInput.builder() .config(S3ECConfig.builder() .keyMaterial(kmsKeyArn).build()) @@ -261,7 +75,7 @@ public void crossLanguageTestKms(LanguageServerTarget encLang, LanguageServerTar String decS3ECId = decClientOutput.getClientId(); GetObjectOutput output = decClient.getObject(GetObjectInput.builder() .clientID(decS3ECId) - .bucket(BUCKET) + .bucket(TestUtils.BUCKET) .key(objectKey) .build()); @@ -271,20 +85,20 @@ public void crossLanguageTestKms(LanguageServerTarget encLang, LanguageServerTar } @ParameterizedTest(name = "{displayName} for Encrypt: {0}, Decrypt: {1}") - @MethodSource("crossLanguageClients") + @MethodSource("software.amazon.encryption.s3.TestUtils#crossLanguageClients") public void crossLanguageTestKmsWithEncCtx(LanguageServerTarget encLang, LanguageServerTarget decLang) { - if (ENCRYPTION_CONTEXT_ON_ENCRYPT_UNSUPPORTED.contains(encLang.getLanguageName())) { + if (TestUtils.ENCRYPTION_CONTEXT_ON_ENCRYPT_UNSUPPORTED.contains(encLang.getLanguageName())) { return; } - S3ECTestServerClient encClient = testServerClientFor(encLang); + S3ECTestServerClient encClient = TestUtils.testServerClientFor(encLang); final String objectKey = "cross-lang-test-key-kms-ec-" + encLang; final String input = "simple-test-input"; final Map encCtx = new HashMap<>(); encCtx.put("user-defined-enc-ctx-key", "user-defined-enc-ctx-value"); encCtx.put("user-defined-enc-ctx-key-2", "user-defined-enc-ctx-value-2"); - final List mdAsList = metadataMapToList(encCtx); + final List mdAsList = TestUtils.metadataMapToList(encCtx); KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(KMS_KEY_ARN) + .kmsKeyId(TestUtils.KMS_KEY_ARN) .build(); CreateClientOutput encClientOutput = encClient.createClient(CreateClientInput.builder() .config(S3ECConfig.builder() @@ -295,11 +109,11 @@ public void crossLanguageTestKmsWithEncCtx(LanguageServerTarget encLang, Languag encClient.putObject(PutObjectInput.builder() .clientID(encS3ECId) .key(objectKey) - .bucket(BUCKET) + .bucket(TestUtils.BUCKET) .metadata(mdAsList) .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) .build()); - S3ECTestServerClient decClient = testServerClientFor(decLang); + S3ECTestServerClient decClient = TestUtils.testServerClientFor(decLang); CreateClientOutput decClientOutput = decClient.createClient(CreateClientInput.builder() .config(S3ECConfig.builder() .keyMaterial(kmsKeyArn).build()) @@ -307,7 +121,7 @@ public void crossLanguageTestKmsWithEncCtx(LanguageServerTarget encLang, Languag String decS3ECId = decClientOutput.getClientId(); GetObjectOutput output = decClient.getObject(GetObjectInput.builder() .clientID(decS3ECId) - .bucket(BUCKET) + .bucket(TestUtils.BUCKET) .key(objectKey) .metadata(mdAsList) .build()); @@ -318,23 +132,23 @@ public void crossLanguageTestKmsWithEncCtx(LanguageServerTarget encLang, Languag } @ParameterizedTest(name = "{displayName} for Encrypt: {0}, Decrypt: {1}") - @MethodSource("crossLanguageClients") + @MethodSource("software.amazon.encryption.s3.TestUtils#crossLanguageClients") public void crossLanguageTestKmsWithSubsetEncCtxFails(LanguageServerTarget encLang, LanguageServerTarget decLang) { - if (ENCRYPTION_CONTEXT_ON_DECRYPT_UNSUPPORTED.contains(decLang.getLanguageName())) { + if (TestUtils.ENCRYPTION_CONTEXT_ON_DECRYPT_UNSUPPORTED.contains(decLang.getLanguageName())) { return; } - if (ENCRYPTION_CONTEXT_ON_ENCRYPT_UNSUPPORTED.contains(encLang.getLanguageName())) { + if (TestUtils.ENCRYPTION_CONTEXT_ON_ENCRYPT_UNSUPPORTED.contains(encLang.getLanguageName())) { return; } - S3ECTestServerClient encClient = testServerClientFor(encLang); + S3ECTestServerClient encClient = TestUtils.testServerClientFor(encLang); final String objectKey = "cross-lang-test-key-kms-ec-subset-fails" + encLang; final String input = "simple-test-input"; final Map encCtx = new HashMap<>(); encCtx.put("user-defined-enc-ctx-key", "user-defined-enc-ctx-value"); encCtx.put("user-defined-enc-ctx-key-2", "user-defined-enc-ctx-value-2"); - final List mdAsList = metadataMapToList(encCtx); + final List mdAsList = TestUtils.metadataMapToList(encCtx); KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(KMS_KEY_ARN) + .kmsKeyId(TestUtils.KMS_KEY_ARN) .build(); CreateClientOutput encClientOutput = encClient.createClient(CreateClientInput.builder() .config(S3ECConfig.builder() @@ -345,11 +159,11 @@ public void crossLanguageTestKmsWithSubsetEncCtxFails(LanguageServerTarget encLa encClient.putObject(PutObjectInput.builder() .clientID(encS3ECId) .key(objectKey) - .bucket(BUCKET) + .bucket(TestUtils.BUCKET) .metadata(mdAsList) .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) .build()); - S3ECTestServerClient decClient = testServerClientFor(decLang); + S3ECTestServerClient decClient = TestUtils.testServerClientFor(decLang); CreateClientOutput decClientOutput = decClient.createClient(CreateClientInput.builder() .config(S3ECConfig.builder() .keyMaterial(kmsKeyArn).build()) @@ -358,12 +172,12 @@ public void crossLanguageTestKmsWithSubsetEncCtxFails(LanguageServerTarget encLa try { decClient.getObject(GetObjectInput.builder() .clientID(decS3ECId) - .bucket(BUCKET) + .bucket(TestUtils.BUCKET) .key(objectKey) .build()); fail("Expected exception!"); } catch (S3EncryptionClientError e) { - if (decLang.languageName.equals(RUBY_V3) || decLang.languageName.equals(RUBY_V2)) { + if (decLang.getLanguageName().equals(TestUtils.RUBY_V3) || decLang.getLanguageName().equals(TestUtils.RUBY_V2)) { assertTrue(e.getMessage().contains("Value of encryption context from envelope does not match the provided encryption context")); } else { assertTrue(e.getMessage().contains("Provided encryption context does not match information retrieved from S3")); @@ -372,20 +186,20 @@ public void crossLanguageTestKmsWithSubsetEncCtxFails(LanguageServerTarget encLa } @ParameterizedTest(name = "{displayName} for Encrypt: {0}, Decrypt: {1}") - @MethodSource("crossLanguageClients") + @MethodSource("software.amazon.encryption.s3.TestUtils#crossLanguageClients") public void crossLanguageTestKmsWithIncorrectEncCtxFails(LanguageServerTarget encLang, LanguageServerTarget decLang) { - if (ENCRYPTION_CONTEXT_ON_DECRYPT_UNSUPPORTED.contains(decLang.getLanguageName())) { + if (TestUtils.ENCRYPTION_CONTEXT_ON_DECRYPT_UNSUPPORTED.contains(decLang.getLanguageName())) { return; } - S3ECTestServerClient encClient = testServerClientFor(encLang); + S3ECTestServerClient encClient = TestUtils.testServerClientFor(encLang); final String objectKey = "cross-lang-test-key-kms-ec-incorrect-fails" + encLang; final String input = "simple-test-input"; final Map encCtx = new HashMap<>(); encCtx.put("user-defined-enc-ctx-key", "user-defined-enc-ctx-value"); encCtx.put("user-defined-enc-ctx-key-2", "user-defined-enc-ctx-value-2"); - final List mdAsList = metadataMapToList(encCtx); + final List mdAsList = TestUtils.metadataMapToList(encCtx); KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(KMS_KEY_ARN) + .kmsKeyId(TestUtils.KMS_KEY_ARN) .build(); CreateClientOutput encClientOutput = encClient.createClient(CreateClientInput.builder() .config(S3ECConfig.builder() @@ -396,11 +210,11 @@ public void crossLanguageTestKmsWithIncorrectEncCtxFails(LanguageServerTarget en encClient.putObject(PutObjectInput.builder() .clientID(encS3ECId) .key(objectKey) - .bucket(BUCKET) + .bucket(TestUtils.BUCKET) .metadata(mdAsList) .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) .build()); - S3ECTestServerClient decClient = testServerClientFor(decLang); + S3ECTestServerClient decClient = TestUtils.testServerClientFor(decLang); CreateClientOutput decClientOutput = decClient.createClient(CreateClientInput.builder() .config(S3ECConfig.builder() .keyMaterial(kmsKeyArn).build()) @@ -409,17 +223,17 @@ public void crossLanguageTestKmsWithIncorrectEncCtxFails(LanguageServerTarget en final Map incorrectEncCtx = new HashMap<>(); incorrectEncCtx.put("this-is-wrong-ec-key", "bad-value"); - var incorrectMdAsList = metadataMapToList(incorrectEncCtx); + var incorrectMdAsList = TestUtils.metadataMapToList(incorrectEncCtx); try { decClient.getObject(GetObjectInput.builder() .clientID(decS3ECId) - .bucket(BUCKET) + .bucket(TestUtils.BUCKET) .key(objectKey) .metadata(incorrectMdAsList) .build()); fail("Expected exception!"); } catch (S3EncryptionClientError e) { - if (decLang.languageName.equals(RUBY_V3) || decLang.languageName.equals(RUBY_V2)) { + if (decLang.getLanguageName().equals(TestUtils.RUBY_V3) || decLang.getLanguageName().equals(TestUtils.RUBY_V2)) { assertTrue(e.getMessage().contains("Value of encryption context from envelope does not match the provided encryption context")); } else { assertTrue(e.getMessage().contains("Provided encryption context does not match information retrieved from S3")); @@ -428,13 +242,13 @@ public void crossLanguageTestKmsWithIncorrectEncCtxFails(LanguageServerTarget en } @ParameterizedTest(name = "{displayName} for Encrypt: Java, Decrypt: {0}") - @MethodSource("clientsForTest") + @MethodSource("software.amazon.encryption.s3.TestUtils#clientsForTest") public void kmsV1Legacy(String language) { - S3ECTestServerClient client = testServerClientFor(serverMap.get(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(KMS_KEY_ARN) + .kmsKeyId(TestUtils.KMS_KEY_ARN) .build(); CreateClientOutput output1 = client.createClient(CreateClientInput.builder() .config(S3ECConfig.builder() @@ -446,23 +260,23 @@ public void kmsV1Legacy(String language) { // Create the object using the old client // V1 Client - EncryptionMaterialsProvider materialsProvider = new KMSEncryptionMaterialsProvider(KMS_KEY_ARN); + EncryptionMaterialsProvider materialsProvider = new KMSEncryptionMaterialsProvider(TestUtils.KMS_KEY_ARN); CryptoConfiguration v1Config = new CryptoConfiguration(CryptoMode.AuthenticatedEncryption) .withStorageMode(CryptoStorageMode.ObjectMetadata) - .withAwsKmsRegion(KMS_REGION); + .withAwsKmsRegion(TestUtils.KMS_REGION); AmazonS3Encryption v1Client = AmazonS3EncryptionClient.encryptionBuilder() .withCryptoConfiguration(v1Config) .withEncryptionMaterials(materialsProvider) .build(); - v1Client.putObject(BUCKET, objectKey, input); + v1Client.putObject(TestUtils.BUCKET, objectKey, input); GetObjectOutput output = client.getObject(GetObjectInput.builder() .clientID(s3ECId) - .bucket(BUCKET) + .bucket(TestUtils.BUCKET) .key(objectKey) .build()); @@ -470,13 +284,13 @@ public void kmsV1Legacy(String language) { } @ParameterizedTest(name = "{displayName} for Encrypt: Java, Decrypt: {0}") - @MethodSource("clientsForTest") + @MethodSource("software.amazon.encryption.s3.TestUtils#clientsForTest") public void kmsV1LegacyWithEncCtx(String language) { - S3ECTestServerClient client = testServerClientFor(serverMap.get(language)); + S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); final String objectKey = "test-key-kms-v1-with-enc-ctx-" + language; final String input = "simple-test-input"; KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(KMS_KEY_ARN) + .kmsKeyId(TestUtils.KMS_KEY_ARN) .build(); CreateClientOutput output1 = client.createClient(CreateClientInput.builder() .config(S3ECConfig.builder() @@ -490,42 +304,42 @@ public void kmsV1LegacyWithEncCtx(String language) { // V1 Client final String ecKey = "user-metadata-key"; final String ecValue = "user-metadata-value-v1"; - KMSEncryptionMaterials kmsMaterials = new KMSEncryptionMaterials(KMS_KEY_ARN); + KMSEncryptionMaterials kmsMaterials = new KMSEncryptionMaterials(TestUtils.KMS_KEY_ARN); kmsMaterials.addDescription(ecKey, ecValue); EncryptionMaterialsProvider materialsProvider = new KMSEncryptionMaterialsProvider(kmsMaterials); CryptoConfiguration v1Config = new CryptoConfiguration(CryptoMode.AuthenticatedEncryption) .withStorageMode(CryptoStorageMode.ObjectMetadata) - .withAwsKmsRegion(KMS_REGION); + .withAwsKmsRegion(TestUtils.KMS_REGION); AmazonS3Encryption v1Client = AmazonS3EncryptionClient.encryptionBuilder() .withCryptoConfiguration(v1Config) .withEncryptionMaterials(materialsProvider) .build(); - v1Client.putObject(BUCKET, objectKey, input); + v1Client.putObject(TestUtils.BUCKET, objectKey, input); final Map encCtx = new HashMap<>(); encCtx.put(ecKey, ecValue); GetObjectOutput output = client.getObject(GetObjectInput.builder() .clientID(s3ECId) - .bucket(BUCKET) + .bucket(TestUtils.BUCKET) .key(objectKey) - .metadata(metadataMapToList(encCtx)) + .metadata(TestUtils.metadataMapToList(encCtx)) .build()); assertEquals(input, new String(output.getBody().array())); } @ParameterizedTest(name = "{displayName} for Encrypt: Java, Decrypt: {0}") - @MethodSource("clientsForTest") + @MethodSource("software.amazon.encryption.s3.TestUtils#clientsForTest") public void kmsV1LegacyFailsWhenLegacyDisabled(String language) { - S3ECTestServerClient client = testServerClientFor(serverMap.get(language)); + S3ECTestServerClient client = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); final String objectKey = "test-key-kms-v1-fails-disabled" + language; final String input = "simple-test-input"; KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(KMS_KEY_ARN) + .kmsKeyId(TestUtils.KMS_KEY_ARN) .build(); CreateClientOutput output1 = client.createClient(CreateClientInput.builder() .config(S3ECConfig.builder() @@ -537,33 +351,33 @@ public void kmsV1LegacyFailsWhenLegacyDisabled(String language) { // Create the object using the old client // V1 Client - EncryptionMaterialsProvider materialsProvider = new KMSEncryptionMaterialsProvider(KMS_KEY_ARN); + EncryptionMaterialsProvider materialsProvider = new KMSEncryptionMaterialsProvider(TestUtils.KMS_KEY_ARN); CryptoConfiguration v1Config = new CryptoConfiguration(CryptoMode.AuthenticatedEncryption) .withStorageMode(CryptoStorageMode.ObjectMetadata) - .withAwsKmsRegion(KMS_REGION); + .withAwsKmsRegion(TestUtils.KMS_REGION); AmazonS3Encryption v1Client = AmazonS3EncryptionClient.encryptionBuilder() .withCryptoConfiguration(v1Config) .withEncryptionMaterials(materialsProvider) .build(); - v1Client.putObject(BUCKET, objectKey, input); + v1Client.putObject(TestUtils.BUCKET, objectKey, input); try { client.getObject(GetObjectInput.builder() .clientID(s3ECId) - .bucket(BUCKET) + .bucket(TestUtils.BUCKET) .key(objectKey) .build()); fail("Expected Exception"); } catch (S3EncryptionClientError e) { - if (language.equals(NET_V3) || language.equals(NET_V2) || language.equals(CPP_V2)) { + if (language.equals(TestUtils.NET_V3) || language.equals(TestUtils.NET_V2) || language.equals(TestUtils.CPP_V2)) { 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)) { + } else if (language.equals(TestUtils.RUBY_V3) || language.equals(TestUtils.RUBY_V2)) { 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 { 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 new file mode 100644 index 00000000..df701583 --- /dev/null +++ b/test-server/java-tests/src/it/java/software/amazon/encryption/s3/TestUtils.java @@ -0,0 +1,235 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.encryption.s3; + +import java.net.Socket; +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.junit.jupiter.params.provider.Arguments; +import com.amazonaws.regions.Region; +import com.amazonaws.regions.Regions; +import software.amazon.smithy.java.aws.client.restjson.RestJsonClientProtocol; +import software.amazon.smithy.java.client.core.ClientConfig; +import software.amazon.smithy.java.client.core.ClientProtocol; +import software.amazon.smithy.java.client.core.endpoint.EndpointResolver; +import software.amazon.encryption.s3.client.S3ECTestServerClient; +import software.amazon.encryption.s3.model.S3ECTestServerApiService; +import software.amazon.smithy.java.http.api.HttpRequest;c +import software.amazon.smithy.java.http.api.HttpResponse; + +public class TestUtils { + // Language constants + public static final String JAVA_V3 = "Java-V3"; + public static final String PYTHON_V3 = "Python-V3"; + public static final String GO_V3 = "Go-V3"; + public static final String CPP_V2 = "CPP-V2"; + public static final String NET_V2 = "NET-V2"; + public static final String NET_V3 = "NET-V3"; + public static final String PHP_V2 = "PHP-V2"; + public static final String PHP_V3 = "PHP-V3"; + public static final String RUBY_V2 = "Ruby-V2"; + public static final String RUBY_V3 = "Ruby-V3"; + + // Test configuration constants + public static final String KMS_KEY_ARN = System.getenv("TEST_SERVER_KMS_KEY_ARN") != null ? + System.getenv("TEST_SERVER_KMS_KEY_ARN") : "arn:aws:kms:us-west-2:370957321024:alias/S3EC-Test-Server-Github-KMS-Key"; + public static final Region KMS_REGION = Region.getRegion(Regions.fromName("us-west-2")); + public static final String BUCKET = System.getenv("TEST_SERVER_S3_BUCKET") != null ? + System.getenv("TEST_SERVER_S3_BUCKET") : "s3ec-test-server-github-bucket"; + + // Sets of unsupported features by language + public static final Set ENCRYPTION_CONTEXT_ON_DECRYPT_UNSUPPORTED = + Set.of(GO_V3, PHP_V2, PHP_V3, NET_V2, NET_V3); + + public static final Set ENCRYPTION_CONTEXT_ON_ENCRYPT_UNSUPPORTED = + Set.of(NET_V2, NET_V3); + + private static final Map serverMap; + + static { + final Map servers = new LinkedHashMap<>(); + servers.put(JAVA_V3, new LanguageServerTarget(JAVA_V3, "8080")); + servers.put(PYTHON_V3, new LanguageServerTarget(PYTHON_V3, "8081")); + servers.put(GO_V3, new LanguageServerTarget(GO_V3, "8082")); + servers.put(NET_V2, new LanguageServerTarget(NET_V2, "8083")); + servers.put(NET_V3, new LanguageServerTarget(NET_V3, "8084")); + servers.put(CPP_V2, new LanguageServerTarget(CPP_V2, "8085")); + servers.put(PHP_V2, new LanguageServerTarget(PHP_V2, "8087")); + servers.put(PHP_V3, new LanguageServerTarget(PHP_V3, "8093")); + servers.put(RUBY_V2, new LanguageServerTarget(RUBY_V2, "8086")); + servers.put(RUBY_V3, new LanguageServerTarget(RUBY_V3, "8092")); + + serverMap = filterServers(servers); + } + + public static class LanguageServerTarget { + private final String baseURI = "http://localhost"; + private String languageName; + private URI serverURI; + + public LanguageServerTarget(String language, String port) { + languageName = language; + serverURI = URI.create(baseURI + ":" + port); + } + + public String getLanguageName() { + return languageName; + } + + public URI getServerURI() { + return serverURI; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + LanguageServerTarget that = (LanguageServerTarget) o; + return Objects.equals(languageName, that.languageName) && Objects.equals(serverURI, that.serverURI); + } + + @Override + public int hashCode() { + return Objects.hash(languageName, serverURI); + } + + @Override + public String toString() { + return languageName; + } + } + + /** + * Filters the available servers based on system property test.filter.servers + * @param allServers Map of all available servers + * @return Filtered map of servers to use for testing + */ + private static Map filterServers(Map allServers) { + final String maybeFilter = System.getProperty("test.filter.servers"); + if (maybeFilter == null || maybeFilter.trim().isEmpty()) { + return allServers; // No filtering - use all servers + } + + final String[] filters = Arrays.stream(maybeFilter.split(",")) + .map(String::trim) + .map(String::toLowerCase) + .toArray(String[]::new); + + return allServers.entrySet().stream() + .filter(entry -> { + String key = entry.getKey().toLowerCase(); + return Arrays.stream(filters).anyMatch(key::contains); + }) + .collect(Collectors.toMap( + Map.Entry::getKey, + Map.Entry::getValue, + (e1, e2) -> e1, // merge function (not really needed) + LinkedHashMap::new // preserve order + )); + } + + /** + * Gets the map of available server targets for testing + * @return Map of language names to server targets + */ + public static Map getServerMap() { + return serverMap; + } + + /** + * Checks if a server is listening on the specified URI + * @param uri The URI to check + * @return true if server is listening, false otherwise + */ + public static boolean serverListening(URI uri) { + try (Socket ignored = new Socket(uri.getHost(), uri.getPort())) { + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * Creates a test server client for the specified language server target + * @param server The language server target + * @return Configured S3ECTestServerClient + */ + public static S3ECTestServerClient testServerClientFor(LanguageServerTarget server) { + S3ECTestServerApiService apiService = S3ECTestServerApiService.instance(); + ClientProtocol rest = new RestJsonClientProtocol(apiService.schema().id()); + return S3ECTestServerClient.builder() + .endpointResolver(EndpointResolver.staticEndpoint(server.serverURI)) + .withConfiguration(ClientConfig.builder() + .service(apiService) + .protocol(rest) + .endpointResolver(EndpointResolver.staticEndpoint(server.serverURI)) + .build()) + .build(); + } + + /** + * Converts a metadata map to a list format for Smithy serialization + * Annoyingly, Smithy doesn't provide an interface for map types + * in HTTP headers, so we have to do the serde ourselves + * @param md The metadata map + * @return List representation of the metadata + */ + public static List metadataMapToList(Map md) { + List mdAsList = new ArrayList<>(md.size()); + for (Map.Entry keyValue : md.entrySet()) { + // Using ":" because Smithy will parse "," into a flattened list + mdAsList.add("[" + keyValue.getKey() + "]:[" + keyValue.getValue() + "]"); + } + return mdAsList; + } + + /** + * Validates that all servers in the server map are running + * @throws RuntimeException if any server is not running + */ + public static void validateServersRunning() { + for (LanguageServerTarget server : serverMap.values()) { + if (!serverListening(server.getServerURI())) { + throw new RuntimeException(String.format("Test Server for %s is not running at endpoint: %s", + server.getLanguageName(), server.getServerURI())); + } + } + } + + /** + * Provides a stream of arguments for parameterized tests that test individual clients + * @return Stream of Arguments containing language names for testing + */ + public static Stream clientsForTest() { + return serverMap.values().stream() + .map(LanguageServerTarget::getLanguageName) + .map(Arguments::of); + } + + /** + * Provides a stream of arguments for parameterized tests that test cross-language compatibility + * @return Stream of Arguments containing pairs of LanguageServerTarget for encryption and decryption + */ + public static Stream crossLanguageClients() { + return serverMap.values().stream() + .flatMap(t1 -> serverMap.values().stream() + .flatMap(t2 -> Stream.of( + Arguments.of(t1, t2) + ))); + } +} From 6754f8c6a1c1650b1ff1ef2d84205a051a15e845 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Thu, 25 Sep 2025 11:20:12 -0700 Subject: [PATCH 2/4] m --- .../src/it/java/software/amazon/encryption/s3/TestUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 df701583..2e78d9e5 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 @@ -26,7 +26,7 @@ import software.amazon.smithy.java.client.core.endpoint.EndpointResolver; import software.amazon.encryption.s3.client.S3ECTestServerClient; import software.amazon.encryption.s3.model.S3ECTestServerApiService; -import software.amazon.smithy.java.http.api.HttpRequest;c +import software.amazon.smithy.java.http.api.HttpRequest; import software.amazon.smithy.java.http.api.HttpResponse; public class TestUtils { From 6e4b8473ef04601df82e9dbbea5315e2e460ada4 Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Thu, 25 Sep 2025 15:08:56 -0700 Subject: [PATCH 3/4] m --- .../amazon/encryption/s3/RoundTripTests.java | 106 +++++++++--------- 1 file changed, 53 insertions(+), 53 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 b2160eb1..93f66450 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 @@ -36,7 +36,7 @@ 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.LanguageServerTarget; +import software.amazon.encryption.s3.TestUtils.*; import com.amazonaws.services.s3.model.EncryptionMaterialsProvider; import com.amazonaws.services.s3.model.KMSEncryptionMaterialsProvider; @@ -44,17 +44,17 @@ public class RoundTripTests { @BeforeAll public static void setup() { - TestUtils.validateServersRunning(); + validateServersRunning(); } @ParameterizedTest(name = "{displayName} for Encrypt: {0}, Decrypt: {1}") @MethodSource("software.amazon.encryption.s3.TestUtils#crossLanguageClients") public void crossLanguageTestKms(LanguageServerTarget encLang, LanguageServerTarget decLang) { - S3ECTestServerClient encClient = TestUtils.testServerClientFor(encLang); + S3ECTestServerClient encClient = testServerClientFor(encLang); final String objectKey = "cross-lang-test-key-" + encLang; final String input = "simple-test-input"; KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(TestUtils.KMS_KEY_ARN) + .kmsKeyId(KMS_KEY_ARN) .build(); CreateClientOutput encClientOutput = encClient.createClient(CreateClientInput.builder() .config(S3ECConfig.builder() @@ -64,10 +64,10 @@ public void crossLanguageTestKms(LanguageServerTarget encLang, LanguageServerTar encClient.putObject(PutObjectInput.builder() .clientID(encS3ECId) .key(objectKey) - .bucket(TestUtils.BUCKET) + .bucket(BUCKET) .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) .build()); - S3ECTestServerClient decClient = TestUtils.testServerClientFor(decLang); + S3ECTestServerClient decClient = testServerClientFor(decLang); CreateClientOutput decClientOutput = decClient.createClient(CreateClientInput.builder() .config(S3ECConfig.builder() .keyMaterial(kmsKeyArn).build()) @@ -75,7 +75,7 @@ public void crossLanguageTestKms(LanguageServerTarget encLang, LanguageServerTar String decS3ECId = decClientOutput.getClientId(); GetObjectOutput output = decClient.getObject(GetObjectInput.builder() .clientID(decS3ECId) - .bucket(TestUtils.BUCKET) + .bucket(BUCKET) .key(objectKey) .build()); @@ -87,18 +87,18 @@ public void crossLanguageTestKms(LanguageServerTarget encLang, LanguageServerTar @ParameterizedTest(name = "{displayName} for Encrypt: {0}, Decrypt: {1}") @MethodSource("software.amazon.encryption.s3.TestUtils#crossLanguageClients") public void crossLanguageTestKmsWithEncCtx(LanguageServerTarget encLang, LanguageServerTarget decLang) { - if (TestUtils.ENCRYPTION_CONTEXT_ON_ENCRYPT_UNSUPPORTED.contains(encLang.getLanguageName())) { + if (ENCRYPTION_CONTEXT_ON_ENCRYPT_UNSUPPORTED.contains(encLang.getLanguageName())) { return; } - S3ECTestServerClient encClient = TestUtils.testServerClientFor(encLang); + S3ECTestServerClient encClient = testServerClientFor(encLang); final String objectKey = "cross-lang-test-key-kms-ec-" + encLang; final String input = "simple-test-input"; final Map encCtx = new HashMap<>(); encCtx.put("user-defined-enc-ctx-key", "user-defined-enc-ctx-value"); encCtx.put("user-defined-enc-ctx-key-2", "user-defined-enc-ctx-value-2"); - final List mdAsList = TestUtils.metadataMapToList(encCtx); + final List mdAsList = metadataMapToList(encCtx); KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(TestUtils.KMS_KEY_ARN) + .kmsKeyId(KMS_KEY_ARN) .build(); CreateClientOutput encClientOutput = encClient.createClient(CreateClientInput.builder() .config(S3ECConfig.builder() @@ -109,11 +109,11 @@ public void crossLanguageTestKmsWithEncCtx(LanguageServerTarget encLang, Languag encClient.putObject(PutObjectInput.builder() .clientID(encS3ECId) .key(objectKey) - .bucket(TestUtils.BUCKET) + .bucket(BUCKET) .metadata(mdAsList) .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) .build()); - S3ECTestServerClient decClient = TestUtils.testServerClientFor(decLang); + S3ECTestServerClient decClient = testServerClientFor(decLang); CreateClientOutput decClientOutput = decClient.createClient(CreateClientInput.builder() .config(S3ECConfig.builder() .keyMaterial(kmsKeyArn).build()) @@ -121,7 +121,7 @@ public void crossLanguageTestKmsWithEncCtx(LanguageServerTarget encLang, Languag String decS3ECId = decClientOutput.getClientId(); GetObjectOutput output = decClient.getObject(GetObjectInput.builder() .clientID(decS3ECId) - .bucket(TestUtils.BUCKET) + .bucket(BUCKET) .key(objectKey) .metadata(mdAsList) .build()); @@ -134,21 +134,21 @@ public void crossLanguageTestKmsWithEncCtx(LanguageServerTarget encLang, Languag @ParameterizedTest(name = "{displayName} for Encrypt: {0}, Decrypt: {1}") @MethodSource("software.amazon.encryption.s3.TestUtils#crossLanguageClients") public void crossLanguageTestKmsWithSubsetEncCtxFails(LanguageServerTarget encLang, LanguageServerTarget decLang) { - if (TestUtils.ENCRYPTION_CONTEXT_ON_DECRYPT_UNSUPPORTED.contains(decLang.getLanguageName())) { + if (ENCRYPTION_CONTEXT_ON_DECRYPT_UNSUPPORTED.contains(decLang.getLanguageName())) { return; } - if (TestUtils.ENCRYPTION_CONTEXT_ON_ENCRYPT_UNSUPPORTED.contains(encLang.getLanguageName())) { + if (ENCRYPTION_CONTEXT_ON_ENCRYPT_UNSUPPORTED.contains(encLang.getLanguageName())) { return; } - S3ECTestServerClient encClient = TestUtils.testServerClientFor(encLang); + S3ECTestServerClient encClient = testServerClientFor(encLang); final String objectKey = "cross-lang-test-key-kms-ec-subset-fails" + encLang; final String input = "simple-test-input"; final Map encCtx = new HashMap<>(); encCtx.put("user-defined-enc-ctx-key", "user-defined-enc-ctx-value"); encCtx.put("user-defined-enc-ctx-key-2", "user-defined-enc-ctx-value-2"); - final List mdAsList = TestUtils.metadataMapToList(encCtx); + final List mdAsList = metadataMapToList(encCtx); KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(TestUtils.KMS_KEY_ARN) + .kmsKeyId(KMS_KEY_ARN) .build(); CreateClientOutput encClientOutput = encClient.createClient(CreateClientInput.builder() .config(S3ECConfig.builder() @@ -159,11 +159,11 @@ public void crossLanguageTestKmsWithSubsetEncCtxFails(LanguageServerTarget encLa encClient.putObject(PutObjectInput.builder() .clientID(encS3ECId) .key(objectKey) - .bucket(TestUtils.BUCKET) + .bucket(BUCKET) .metadata(mdAsList) .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) .build()); - S3ECTestServerClient decClient = TestUtils.testServerClientFor(decLang); + S3ECTestServerClient decClient = testServerClientFor(decLang); CreateClientOutput decClientOutput = decClient.createClient(CreateClientInput.builder() .config(S3ECConfig.builder() .keyMaterial(kmsKeyArn).build()) @@ -172,12 +172,12 @@ public void crossLanguageTestKmsWithSubsetEncCtxFails(LanguageServerTarget encLa try { decClient.getObject(GetObjectInput.builder() .clientID(decS3ECId) - .bucket(TestUtils.BUCKET) + .bucket(BUCKET) .key(objectKey) .build()); fail("Expected exception!"); } catch (S3EncryptionClientError e) { - if (decLang.getLanguageName().equals(TestUtils.RUBY_V3) || decLang.getLanguageName().equals(TestUtils.RUBY_V2)) { + if (decLang.getLanguageName().equals(RUBY_V3) || decLang.getLanguageName().equals(RUBY_V2)) { assertTrue(e.getMessage().contains("Value of encryption context from envelope does not match the provided encryption context")); } else { assertTrue(e.getMessage().contains("Provided encryption context does not match information retrieved from S3")); @@ -188,18 +188,18 @@ public void crossLanguageTestKmsWithSubsetEncCtxFails(LanguageServerTarget encLa @ParameterizedTest(name = "{displayName} for Encrypt: {0}, Decrypt: {1}") @MethodSource("software.amazon.encryption.s3.TestUtils#crossLanguageClients") public void crossLanguageTestKmsWithIncorrectEncCtxFails(LanguageServerTarget encLang, LanguageServerTarget decLang) { - if (TestUtils.ENCRYPTION_CONTEXT_ON_DECRYPT_UNSUPPORTED.contains(decLang.getLanguageName())) { + if (ENCRYPTION_CONTEXT_ON_DECRYPT_UNSUPPORTED.contains(decLang.getLanguageName())) { return; } - S3ECTestServerClient encClient = TestUtils.testServerClientFor(encLang); + S3ECTestServerClient encClient = testServerClientFor(encLang); final String objectKey = "cross-lang-test-key-kms-ec-incorrect-fails" + encLang; final String input = "simple-test-input"; final Map encCtx = new HashMap<>(); encCtx.put("user-defined-enc-ctx-key", "user-defined-enc-ctx-value"); encCtx.put("user-defined-enc-ctx-key-2", "user-defined-enc-ctx-value-2"); - final List mdAsList = TestUtils.metadataMapToList(encCtx); + final List mdAsList = metadataMapToList(encCtx); KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(TestUtils.KMS_KEY_ARN) + .kmsKeyId(KMS_KEY_ARN) .build(); CreateClientOutput encClientOutput = encClient.createClient(CreateClientInput.builder() .config(S3ECConfig.builder() @@ -210,11 +210,11 @@ public void crossLanguageTestKmsWithIncorrectEncCtxFails(LanguageServerTarget en encClient.putObject(PutObjectInput.builder() .clientID(encS3ECId) .key(objectKey) - .bucket(TestUtils.BUCKET) + .bucket(BUCKET) .metadata(mdAsList) .body(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8))) .build()); - S3ECTestServerClient decClient = TestUtils.testServerClientFor(decLang); + S3ECTestServerClient decClient = testServerClientFor(decLang); CreateClientOutput decClientOutput = decClient.createClient(CreateClientInput.builder() .config(S3ECConfig.builder() .keyMaterial(kmsKeyArn).build()) @@ -223,17 +223,17 @@ public void crossLanguageTestKmsWithIncorrectEncCtxFails(LanguageServerTarget en final Map incorrectEncCtx = new HashMap<>(); incorrectEncCtx.put("this-is-wrong-ec-key", "bad-value"); - var incorrectMdAsList = TestUtils.metadataMapToList(incorrectEncCtx); + var incorrectMdAsList = metadataMapToList(incorrectEncCtx); try { decClient.getObject(GetObjectInput.builder() .clientID(decS3ECId) - .bucket(TestUtils.BUCKET) + .bucket(BUCKET) .key(objectKey) .metadata(incorrectMdAsList) .build()); fail("Expected exception!"); } catch (S3EncryptionClientError e) { - if (decLang.getLanguageName().equals(TestUtils.RUBY_V3) || decLang.getLanguageName().equals(TestUtils.RUBY_V2)) { + if (decLang.getLanguageName().equals(RUBY_V3) || decLang.getLanguageName().equals(RUBY_V2)) { assertTrue(e.getMessage().contains("Value of encryption context from envelope does not match the provided encryption context")); } else { assertTrue(e.getMessage().contains("Provided encryption context does not match information retrieved from S3")); @@ -244,11 +244,11 @@ 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 = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); + S3ECTestServerClient client = testServerClientFor(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) + .kmsKeyId(KMS_KEY_ARN) .build(); CreateClientOutput output1 = client.createClient(CreateClientInput.builder() .config(S3ECConfig.builder() @@ -260,23 +260,23 @@ public void kmsV1Legacy(String language) { // Create the object using the old client // V1 Client - EncryptionMaterialsProvider materialsProvider = new KMSEncryptionMaterialsProvider(TestUtils.KMS_KEY_ARN); + EncryptionMaterialsProvider materialsProvider = new KMSEncryptionMaterialsProvider(KMS_KEY_ARN); CryptoConfiguration v1Config = new CryptoConfiguration(CryptoMode.AuthenticatedEncryption) .withStorageMode(CryptoStorageMode.ObjectMetadata) - .withAwsKmsRegion(TestUtils.KMS_REGION); + .withAwsKmsRegion(KMS_REGION); AmazonS3Encryption v1Client = AmazonS3EncryptionClient.encryptionBuilder() .withCryptoConfiguration(v1Config) .withEncryptionMaterials(materialsProvider) .build(); - v1Client.putObject(TestUtils.BUCKET, objectKey, input); + v1Client.putObject(BUCKET, objectKey, input); GetObjectOutput output = client.getObject(GetObjectInput.builder() .clientID(s3ECId) - .bucket(TestUtils.BUCKET) + .bucket(BUCKET) .key(objectKey) .build()); @@ -286,11 +286,11 @@ 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 = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); + S3ECTestServerClient client = testServerClientFor(getServerMap().get(language)); final String objectKey = "test-key-kms-v1-with-enc-ctx-" + language; final String input = "simple-test-input"; KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(TestUtils.KMS_KEY_ARN) + .kmsKeyId(KMS_KEY_ARN) .build(); CreateClientOutput output1 = client.createClient(CreateClientInput.builder() .config(S3ECConfig.builder() @@ -304,29 +304,29 @@ public void kmsV1LegacyWithEncCtx(String language) { // V1 Client final String ecKey = "user-metadata-key"; final String ecValue = "user-metadata-value-v1"; - KMSEncryptionMaterials kmsMaterials = new KMSEncryptionMaterials(TestUtils.KMS_KEY_ARN); + KMSEncryptionMaterials kmsMaterials = new KMSEncryptionMaterials(KMS_KEY_ARN); kmsMaterials.addDescription(ecKey, ecValue); EncryptionMaterialsProvider materialsProvider = new KMSEncryptionMaterialsProvider(kmsMaterials); CryptoConfiguration v1Config = new CryptoConfiguration(CryptoMode.AuthenticatedEncryption) .withStorageMode(CryptoStorageMode.ObjectMetadata) - .withAwsKmsRegion(TestUtils.KMS_REGION); + .withAwsKmsRegion(KMS_REGION); AmazonS3Encryption v1Client = AmazonS3EncryptionClient.encryptionBuilder() .withCryptoConfiguration(v1Config) .withEncryptionMaterials(materialsProvider) .build(); - v1Client.putObject(TestUtils.BUCKET, objectKey, input); + v1Client.putObject(BUCKET, objectKey, input); final Map encCtx = new HashMap<>(); encCtx.put(ecKey, ecValue); GetObjectOutput output = client.getObject(GetObjectInput.builder() .clientID(s3ECId) - .bucket(TestUtils.BUCKET) + .bucket(BUCKET) .key(objectKey) - .metadata(TestUtils.metadataMapToList(encCtx)) + .metadata(metadataMapToList(encCtx)) .build()); assertEquals(input, new String(output.getBody().array())); @@ -335,11 +335,11 @@ 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 = TestUtils.testServerClientFor(TestUtils.getServerMap().get(language)); + S3ECTestServerClient client = testServerClientFor(getServerMap().get(language)); final String objectKey = "test-key-kms-v1-fails-disabled" + language; final String input = "simple-test-input"; KeyMaterial kmsKeyArn = KeyMaterial.builder() - .kmsKeyId(TestUtils.KMS_KEY_ARN) + .kmsKeyId(KMS_KEY_ARN) .build(); CreateClientOutput output1 = client.createClient(CreateClientInput.builder() .config(S3ECConfig.builder() @@ -351,33 +351,33 @@ public void kmsV1LegacyFailsWhenLegacyDisabled(String language) { // Create the object using the old client // V1 Client - EncryptionMaterialsProvider materialsProvider = new KMSEncryptionMaterialsProvider(TestUtils.KMS_KEY_ARN); + EncryptionMaterialsProvider materialsProvider = new KMSEncryptionMaterialsProvider(KMS_KEY_ARN); CryptoConfiguration v1Config = new CryptoConfiguration(CryptoMode.AuthenticatedEncryption) .withStorageMode(CryptoStorageMode.ObjectMetadata) - .withAwsKmsRegion(TestUtils.KMS_REGION); + .withAwsKmsRegion(KMS_REGION); AmazonS3Encryption v1Client = AmazonS3EncryptionClient.encryptionBuilder() .withCryptoConfiguration(v1Config) .withEncryptionMaterials(materialsProvider) .build(); - v1Client.putObject(TestUtils.BUCKET, objectKey, input); + v1Client.putObject(BUCKET, objectKey, input); try { client.getObject(GetObjectInput.builder() .clientID(s3ECId) - .bucket(TestUtils.BUCKET) + .bucket(BUCKET) .key(objectKey) .build()); fail("Expected Exception"); } catch (S3EncryptionClientError e) { - if (language.equals(TestUtils.NET_V3) || language.equals(TestUtils.NET_V2) || language.equals(TestUtils.CPP_V2)) { + if (language.equals(NET_V3) || language.equals(NET_V2) || language.equals(CPP_V2)) { assertTrue(e.getMessage().contains( "The requested object is encrypted with V1 encryption schemas that have been disabled by client configuration" )); - } else if (language.equals(TestUtils.RUBY_V3) || language.equals(TestUtils.RUBY_V2)) { + } else if (language.equals(RUBY_V3) || language.equals(RUBY_V2)) { 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 { assertTrue(e.getMessage().contains("Enable legacy wrapping algorithms to use legacy key wrapping algorithm: kms")); From 4f3d29973fbd38513e03a5f81cf9024c1228fe9b Mon Sep 17 00:00:00 2001 From: Lucas McDonald Date: Thu, 25 Sep 2025 17:00:09 -0700 Subject: [PATCH 4/4] m --- .../it/java/software/amazon/encryption/s3/RoundTripTests.java | 1 + 1 file changed, 1 insertion(+) 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 93f66450..3b6b664d 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 @@ -8,6 +8,7 @@ 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;