From 7c74a6ae0c883186abcac959007f8468b83bfe12 Mon Sep 17 00:00:00 2001 From: Rishav karanjit Date: Thu, 11 Jun 2026 15:00:21 -0700 Subject: [PATCH 1/6] chore: add test for check for early returns (#94) --- .github/workflows/all-ci.yml | 44 +-- .github/workflows/python-integ.yml | 10 +- .github/workflows/python-perf.yml | 10 +- .github/workflows/test-server.yml | 10 +- .gitmodules | 10 +- .../s3/BleichenbacherOracleTests.java | 277 ++++++++++++++++++ .../s3ec-v3-transition-branch | 2 +- .../net-v4-server/s3ec-net-v4-improved | 2 +- 8 files changed, 321 insertions(+), 44 deletions(-) create mode 100644 test-server/java-tests/src/it/java/software/amazon/encryption/s3/BleichenbacherOracleTests.java diff --git a/.github/workflows/all-ci.yml b/.github/workflows/all-ci.yml index d8c92bb9..85332d55 100644 --- a/.github/workflows/all-ci.yml +++ b/.github/workflows/all-ci.yml @@ -2,7 +2,7 @@ name: All CI on: push: - branches: [ main, staging ] + # branches: [ main, staging ] pull_request: workflow_dispatch: inputs: @@ -13,9 +13,9 @@ on: type: string jobs: - python-lint: - name: Lint - uses: ./.github/workflows/lint.yml + # python-lint: + # name: Lint + # uses: ./.github/workflows/lint.yml run-test-server: permissions: @@ -27,25 +27,25 @@ jobs: python-version: ${{ inputs.python-version || '3.11' }} secrets: inherit - python-integ: - permissions: - id-token: write - contents: read - name: Python Integration Tests - uses: ./.github/workflows/python-integ.yml - with: - python-version: ${{ inputs.python-version || '3.11' }} - secrets: inherit + # python-integ: + # permissions: + # id-token: write + # contents: read + # name: Python Integration Tests + # uses: ./.github/workflows/python-integ.yml + # with: + # python-version: ${{ inputs.python-version || '3.11' }} + # secrets: inherit - python-perf: - permissions: - id-token: write - contents: read - name: Python Performance Tests - uses: ./.github/workflows/python-perf.yml - with: - python-version: ${{ inputs.python-version || '3.11' }} - secrets: inherit + # python-perf: + # permissions: + # id-token: write + # contents: read + # name: Python Performance Tests + # uses: ./.github/workflows/python-perf.yml + # with: + # python-version: ${{ inputs.python-version || '3.11' }} + # secrets: inherit run-duvet: permissions: diff --git a/.github/workflows/python-integ.yml b/.github/workflows/python-integ.yml index 7c22d3e4..475f0136 100644 --- a/.github/workflows/python-integ.yml +++ b/.github/workflows/python-integ.yml @@ -48,12 +48,12 @@ jobs: - name: Install dependencies run: make install - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v6 + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v2 with: - special-characters-workaround: "true" - role-to-assume: arn:aws:iam::370957321024:role/S3EC-Python-Github-test-role - aws-region: us-west-2 + role-to-assume: arn:aws:iam::${{ secrets.CI_AWS_ACCOUNT_ID }}:role/service-role/${{ vars.CI_AWS_ROLE }} + role-session-name: S3EC-Github-CI-Tests + aws-region: ${{ vars.CI_AWS_REGION }} - name: Run unit tests run: uv run pytest test/ --ignore=test/integration/ --ignore=test/performance/ --verbose --cov=src/s3_encryption --cov-report=term-missing --cov-report=html:coverage-unit --cov-fail-under=89 diff --git a/.github/workflows/python-perf.yml b/.github/workflows/python-perf.yml index 38bddb56..8752dc79 100644 --- a/.github/workflows/python-perf.yml +++ b/.github/workflows/python-perf.yml @@ -41,12 +41,12 @@ jobs: - name: Install dependencies run: make install - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v6 + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v2 with: - special-characters-workaround: "true" - role-to-assume: arn:aws:iam::370957321024:role/S3EC-Python-Github-test-role - aws-region: us-west-2 + role-to-assume: arn:aws:iam::${{ secrets.CI_AWS_ACCOUNT_ID }}:role/service-role/${{ vars.CI_AWS_ROLE }} + role-session-name: S3EC-Github-CI-Tests + aws-region: ${{ vars.CI_AWS_REGION }} - name: Run performance tests run: make test-perf diff --git a/.github/workflows/test-server.yml b/.github/workflows/test-server.yml index b60c2167..58800464 100644 --- a/.github/workflows/test-server.yml +++ b/.github/workflows/test-server.yml @@ -99,12 +99,12 @@ jobs: restore-keys: | ${{ runner.os }}-gradle- - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v6 + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v2 with: - special-characters-workaround: "true" - role-to-assume: arn:aws:iam::370957321024:role/S3EC-Python-Github-test-role - aws-region: us-west-2 + role-to-assume: arn:aws:iam::${{ secrets.CI_AWS_ACCOUNT_ID }}:role/service-role/${{ vars.CI_AWS_ROLE }} + role-session-name: S3EC-Github-CI-Tests + aws-region: ${{ vars.CI_AWS_REGION }} - name: Build the servers run: cd test-server && make build-all-servers diff --git a/.gitmodules b/.gitmodules index 162cd457..a2d0d7d5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -27,17 +27,17 @@ url = git@github.com:awslabs/private-aws-encryption-sdk-specification-staging.git branch = fire-egg-staging [submodule "test-server/net-v4-server/s3ec-net-v4-improved"] - path = test-server/net-v4-server/s3ec-net-v4-improved - url = https://github.com/aws/amazon-s3-encryption-client-dotnet.git - branch = main + path = test-server/net-v4-server/s3ec-net-v4-improved + url = https://github.com/aws/private-amazon-s3-encryption-client-dotnet-staging.git + branch = main-fix-Bleichenbacher [submodule "test-server/go-v3-transition-server/local-go-s3ec"] path = test-server/go-v3-transition-server/local-go-s3ec url = https://github.com/aws/amazon-s3-encryption-client-go branch = main [submodule "test-server/net-v3-transition-server/s3ec-v3-transition-branch"] path = test-server/net-v3-transition-server/s3ec-v3-transition-branch - url = https://github.com/aws/amazon-s3-encryption-client-dotnet.git - branch = v4sdk-development + url = https://github.com/aws/private-amazon-s3-encryption-client-dotnet-staging.git + branch = v4sdk-fix-Bleichenbacher [submodule "test-server/cpp-v2-transition-server/aws-sdk-cpp"] path = test-server/cpp-v2-transition-server/aws-sdk-cpp url = git@github.com:aws/aws-sdk-cpp.git diff --git a/test-server/java-tests/src/it/java/software/amazon/encryption/s3/BleichenbacherOracleTests.java b/test-server/java-tests/src/it/java/software/amazon/encryption/s3/BleichenbacherOracleTests.java new file mode 100644 index 00000000..89128a50 --- /dev/null +++ b/test-server/java-tests/src/it/java/software/amazon/encryption/s3/BleichenbacherOracleTests.java @@ -0,0 +1,277 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.encryption.s3; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static software.amazon.encryption.s3.TestUtils.*; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.util.ArrayList; +import java.util.Base64; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import software.amazon.awssdk.core.ResponseInputStream; +import software.amazon.awssdk.core.sync.RequestBody; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.GetObjectResponse; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import software.amazon.encryption.s3.TestUtils.LanguageServerTarget; +import software.amazon.encryption.s3.client.S3ECTestServerClient; +import software.amazon.encryption.s3.model.CommitmentPolicy; +import software.amazon.encryption.s3.model.CreateClientInput; +import software.amazon.encryption.s3.model.EncryptionAlgorithm; +import software.amazon.encryption.s3.model.GetObjectInput; +import software.amazon.encryption.s3.model.InstructionFileConfig; +import software.amazon.encryption.s3.model.KeyMaterial; +import software.amazon.encryption.s3.model.PutObjectInput; +import software.amazon.encryption.s3.model.S3ECConfig; +import software.amazon.encryption.s3.model.S3EncryptionClientError; + +/** + * Tests that verify the Bleichenbacher padding oracle does not exist across all + * RSA-supporting runtimes and commitment policy configurations. + */ +public class BleichenbacherOracleTests { + + private static KeyPair rsaKeyPair; + private static S3Client plaintextS3; + private static final ObjectMapper MAPPER = new ObjectMapper(); + private static final List createdKeys = Collections.synchronizedList(new ArrayList<>()); + + @BeforeAll + public static void setup() throws Exception { + validateServersRunning(); + KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA"); + keyPairGen.initialize(2048); + rsaKeyPair = keyPairGen.generateKeyPair(); + plaintextS3 = S3Client.create(); + } + + @AfterAll + public static void cleanup() { + for (String key : createdKeys) { + try { + plaintextS3.deleteObject(b -> b.bucket(BUCKET).key(key)); + } catch (Exception ignored) { + } + } + } + + /** + * Represents a client configuration to test against. + */ + static class ConfigCase { + final String name; + final boolean legacyWrapping; + final CommitmentPolicy policy; + final EncryptionAlgorithm algo; + + ConfigCase(String name, boolean legacyWrapping, CommitmentPolicy policy, EncryptionAlgorithm algo) { + this.name = name; + this.legacyWrapping = legacyWrapping; + this.policy = policy; + this.algo = algo; + } + + @Override + public String toString() { return name; } + } + + /** + * Provides a matrix of (runtime x config) for parameterized tests. + * Transition versions only support FORBID_ENCRYPT_ALLOW_DECRYPT with GCM (no key commitment), + * so they get a reduced config set. + */ + static Stream rsaRuntimeAndPolicyMatrix() { + // All configs to test + List allConfigs = List.of( + new ConfigCase("GCM-forbid-encrypt-allow-decrypt", false, CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT, EncryptionAlgorithm.ALG_AES_256_GCM_IV12_TAG16_NO_KDF), + new ConfigCase("KC-GCM-require-encrypt-allow-decrypt", false, CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT, EncryptionAlgorithm.ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY), + new ConfigCase("KC-GCM-require-encrypt-require-decrypt", false, CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT, EncryptionAlgorithm.ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY) + ); + + // Transition versions can only use FORBID_ENCRYPT_ALLOW_DECRYPT + List transitionConfigs = allConfigs.stream() + .filter(c -> c.policy == CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT) + .toList(); + + // For each RSA-capable runtime, pair it with the appropriate config set + return clientsRawRsaForTest() + .flatMap(langArg -> { + LanguageServerTarget lang = (LanguageServerTarget) langArg.get()[0]; + // Transition versions get fewer configs; improved versions get all + List configs = TRANSITION_VERSIONS.contains(lang.getLanguageName()) + ? transitionConfigs + : allConfigs; + return configs.stream().map(cfg -> Arguments.of(lang, cfg)); + }); + } + + /** + * For each (runtime, commitmentPolicy) combination: + * 1. Encrypt an object with RSA-OAEP + * 2. Copy it with V1 metadata (downgrade x-amz-key-v2 → x-amz-key) + * 3. Upload a second object with a known-valid PKCS#1v1.5 ciphertext in x-amz-key + * 4. Attempt to decrypt both with legacy disabled + * 5. Assert: the two produce the SAME error (proving the oracle is mitigated) + */ + @ParameterizedTest(name = "{0} / {1}") + @MethodSource("rsaRuntimeAndPolicyMatrix") + public void oracleDistinguishableErrorsMetaData(LanguageServerTarget language, ConfigCase configCase) throws Exception { + verifyNoOracle(language, configCase, "MetaData", null, this::uploadV1Object); + } + + /** + * Same as oracleDistinguishableErrorsMetaData but stores V1 metadata in an instruction file + * instead of object metadata. Verifies the oracle mitigation applies equally to + * the instruction file code path. + */ + @ParameterizedTest(name = "InstructionFile: {0} / {1}") + @MethodSource("rsaRuntimeAndPolicyMatrix") + public void oracleDistinguishableErrorsInstructionFile(LanguageServerTarget language, ConfigCase configCase) throws Exception { + if (INSTRUCTION_FILE_GET_UNSUPPORTED.contains(language.getLanguageName())) { + org.junit.jupiter.api.Assumptions.assumeTrue(false, language.getLanguageName() + " does not support instruction file get"); + } + verifyNoOracle(language, configCase, "InstructionFile", + InstructionFileConfig.builder().enableInstructionFilePutObject(true).build(), + this::uploadV1InstructionFileObject); + } + + @FunctionalInterface + private interface V1Uploader { + void upload(String key, byte[] body, String wrappedKey, String iv, String matdesc) throws Exception; + } + + private void verifyNoOracle(LanguageServerTarget language, ConfigCase configCase, String label, InstructionFileConfig instructionFileConfig, V1Uploader uploader) throws Exception { + S3ECTestServerClient client = testServerClientFor(language); + + KeyMaterial rsaKeyMaterial = KeyMaterial.builder() + .rsaKey(ByteBuffer.wrap(rsaKeyPair.getPrivate().getEncoded())) + .build(); + + S3ECConfig.Builder configBuilder = S3ECConfig.builder() + .enableLegacyWrappingAlgorithms(configCase.legacyWrapping) + .encryptionAlgorithm(configCase.algo) + .commitmentPolicy(configCase.policy) + .keyMaterial(rsaKeyMaterial); + if (instructionFileConfig != null) { + configBuilder.instructionFileConfig(instructionFileConfig); + } + S3ECConfig config = configBuilder.build(); + + String clientId = client.createClient(CreateClientInput.builder().config(config).build()).getClientId(); + + String suffix = language.getLanguageName() + "-" + configCase.name + "-" + label; + + // Encrypt with RSA-OAEP + final String originalKey = appendTestSuffix("bleichenbacher-original-" + suffix); + createdKeys.add(originalKey); + client.putObject(PutObjectInput.builder() + .clientID(clientId) + .bucket(BUCKET) + .key(originalKey) + .body(ByteBuffer.wrap("secret".getBytes(StandardCharsets.UTF_8))) + .build()); + + // Use random bytes for the invalid PKCS#1 padding + String wrappedKey = Base64.getEncoder().encodeToString(new byte[256]); + String iv = Base64.getEncoder().encodeToString(new byte[16]); + String matdesc = "{}"; + + // Download raw encrypted body + byte[] rawBody; + try (ResponseInputStream s3Object = plaintextS3.getObject(b -> b.bucket(BUCKET).key(originalKey))) { + rawBody = s3Object.readAllBytes(); + } + + // Upload with V1 wrapping with invalid PKCS#1 padding + final String invalidPaddingKey = appendTestSuffix("bleichenbacher-invalid-" + suffix); + createdKeys.add(invalidPaddingKey); + uploader.upload(invalidPaddingKey, rawBody, wrappedKey, iv, matdesc); + + // Upload with V1 wrapping (known VALID PKCS#1v1.5 ciphertext) + final String validPaddingKey = appendTestSuffix("bleichenbacher-valid-" + suffix); + createdKeys.add(validPaddingKey); + javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("RSA/ECB/PKCS1Padding"); + cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, rsaKeyPair.getPublic()); + byte[] validPkcs1Ciphertext = cipher.doFinal(new byte[32]); + String validPkcs1Base64 = Base64.getEncoder().encodeToString(validPkcs1Ciphertext); + uploader.upload(validPaddingKey, rawBody, validPkcs1Base64, iv, matdesc); + + // Attempt decrypt of both — should get the same error + String errorInvalid = getDecryptError(client, clientId, invalidPaddingKey); + String errorValid = getDecryptError(client, clientId, validPaddingKey); + + System.out.printf("[BleichenbacherOracleTests][%s][%s][%s] Invalid padding error: %s%n", label, language.getLanguageName(), configCase.name, errorInvalid); + System.out.printf("[BleichenbacherOracleTests][%s][%s][%s] Valid padding error: %s%n", label, language.getLanguageName(), configCase.name, errorValid); + + assertNotEquals("NO_ERROR", errorInvalid, + String.format("[%s][%s][%s] Expected decryption to fail for invalid padding object but it succeeded", + label, language.getLanguageName(), configCase.name)); + assertNotEquals("NO_ERROR", errorValid, + String.format("[%s][%s][%s] Expected decryption to fail for valid padding object but it succeeded", + label, language.getLanguageName(), configCase.name)); + + assertEquals(errorInvalid, errorValid, + String.format("[%s][%s][%s] Errors differ for valid/invalid PKCS#1 padding — oracle still exists!", + label, language.getLanguageName(), configCase.name)); + System.out.printf("[BleichenbacherOracleTests][%s][%s][%s] PASSED — no oracle%n", label, language.getLanguageName(), configCase.name); + } + + private void uploadV1Object(String key, byte[] body, String wrappedKey, String iv, String matdesc) { + Map metadata = new HashMap<>(); + metadata.put("x-amz-key", wrappedKey); + metadata.put("x-amz-iv", iv); + metadata.put("x-amz-matdesc", matdesc != null ? matdesc : "{}"); + + plaintextS3.putObject(b -> b.bucket(BUCKET).key(key).metadata(metadata).contentLength((long) body.length), + RequestBody.fromBytes(body)); + } + + private String getDecryptError(S3ECTestServerClient client, String clientId, String key) { + try { + client.getObject(GetObjectInput.builder() + .clientID(clientId) + .bucket(BUCKET) + .key(key) + .build()); + return "NO_ERROR"; + } catch (S3EncryptionClientError e) { + return e.getMessage(); + } catch (Exception e) { + return "UNEXPECTED: " + e.getClass().getSimpleName() + ": " + e.getMessage(); + } + } + + private void uploadV1InstructionFileObject(String key, byte[] body, String wrappedKey, String iv, String matdesc) throws Exception { + // Upload body with NO encryption metadata in object metadata + plaintextS3.putObject(b -> b.bucket(BUCKET).key(key).contentLength((long) body.length), + RequestBody.fromBytes(body)); + + // Upload .instruction file with V1 metadata as JSON + Map instructionMap = new HashMap<>(); + instructionMap.put("x-amz-key", wrappedKey); + instructionMap.put("x-amz-iv", iv); + instructionMap.put("x-amz-matdesc", matdesc != null ? matdesc : "{}"); + String instructionJson = MAPPER.writeValueAsString(instructionMap); + plaintextS3.putObject(b -> b.bucket(BUCKET).key(key + ".instruction"), + RequestBody.fromString(instructionJson)); + createdKeys.add(key + ".instruction"); + } +} diff --git a/test-server/net-v3-transition-server/s3ec-v3-transition-branch b/test-server/net-v3-transition-server/s3ec-v3-transition-branch index 7a552940..fe9de0ee 160000 --- a/test-server/net-v3-transition-server/s3ec-v3-transition-branch +++ b/test-server/net-v3-transition-server/s3ec-v3-transition-branch @@ -1 +1 @@ -Subproject commit 7a55294068bb3bb7f96226efd6d9edcd1057184b +Subproject commit fe9de0eee5c819075098e6de79065f5a722784b4 diff --git a/test-server/net-v4-server/s3ec-net-v4-improved b/test-server/net-v4-server/s3ec-net-v4-improved index 9b628b06..f3920f60 160000 --- a/test-server/net-v4-server/s3ec-net-v4-improved +++ b/test-server/net-v4-server/s3ec-net-v4-improved @@ -1 +1 @@ -Subproject commit 9b628b06e5c1bf12696c752afb2631c38cae11f9 +Subproject commit f3920f609583e1c24613efbf4efa37b40e6336f5 From c13cfc1b6a46c05760956024a653bd21bbf58dc4 Mon Sep 17 00:00:00 2001 From: Rishav karanjit Date: Fri, 12 Jun 2026 13:49:58 -0700 Subject: [PATCH 2/6] chore: add negative testing for RSA decrypt (#98) --- .../s3/RsaV1LegacyDecryptTests.java | 52 ++++++++++++++++++- .../amazon/encryption/s3/TestUtils.java | 16 ++++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/test-server/java-tests/src/it/java/software/amazon/encryption/s3/RsaV1LegacyDecryptTests.java b/test-server/java-tests/src/it/java/software/amazon/encryption/s3/RsaV1LegacyDecryptTests.java index eb8930e7..fcb0c3e8 100644 --- a/test-server/java-tests/src/it/java/software/amazon/encryption/s3/RsaV1LegacyDecryptTests.java +++ b/test-server/java-tests/src/it/java/software/amazon/encryption/s3/RsaV1LegacyDecryptTests.java @@ -6,7 +6,8 @@ package software.amazon.encryption.s3; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; +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.charset.StandardCharsets; @@ -122,4 +123,53 @@ void canDecryptV1RsaObjectWithLegacyEnabled(LanguageServerTarget language, Strin assertEquals(INPUT, StandardCharsets.UTF_8.decode(output.getBody()).toString()); } + + @ParameterizedTest(name = "Encrypt: Java-V1-RSA, Decrypt: {0} / {1}") + @MethodSource("rsaRuntimeAndPolicyMatrix") + void cannotDecryptV1RsaObjectWithLegacyDisabled(LanguageServerTarget language, String configName, + CommitmentPolicy policy, EncryptionAlgorithm algo) { + S3ECTestServerClient client = testServerClientFor(language); + + KeyMaterial rsaKeyMaterial = KeyMaterial.builder() + .rsaKey(ByteBuffer.wrap(rsaKeyPair.getPrivate().getEncoded())) + .build(); + String clientId; + // Some languages use a single SecurityProfile toggle, so both must be false + if (LANGUAGES_WITH_SECURITY_PROFILE.contains(language.getLanguageName())) { + clientId = client.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .keyMaterial(rsaKeyMaterial) + .commitmentPolicy(policy) + .encryptionAlgorithm(algo) + .enableLegacyUnauthenticatedModes(false) + .enableLegacyWrappingAlgorithms(false) + .build()) + .build()).getClientId(); + } else { + clientId = client.createClient(CreateClientInput.builder() + .config(S3ECConfig.builder() + .keyMaterial(rsaKeyMaterial) + .commitmentPolicy(policy) + .encryptionAlgorithm(algo) + .enableLegacyUnauthenticatedModes(true) + .enableLegacyWrappingAlgorithms(false) + .build()) + .build()).getClientId(); + } + + try { + client.getObject(GetObjectInput.builder() + .clientID(clientId) + .bucket(BUCKET) + .key(v1ObjectKey) + .build()); + fail("Expected exception!"); + } catch (S3EncryptionClientError e) { + if (LANGUAGES_WITH_SECURITY_PROFILE.contains(language.getLanguageName())) { + assertTrue(e.getMessage().contains("The requested object is encrypted with V1 encryption schemas that have been disabled by client configuration"), "Actual error: " + e.getMessage()); + } else { + assertTrue(e.getMessage().contains("Enable legacy wrapping algorithms to use legacy key wrapping algorithm: RSA"), "Actual error: " + e.getMessage()); + } + } + } } 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 5f4ce9d6..180128a6 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 @@ -150,6 +150,22 @@ public class TestUtils { PHP_V3 ); + // Languages that use a single SecurityProfile toggle instead of separate + // enableLegacyUnauthenticatedModes / enableLegacyWrappingAlgorithms flags. + public static final Set LANGUAGES_WITH_SECURITY_PROFILE = + Set.of( + RUBY_V2_TRANSITION, + RUBY_V3, + PHP_V2_TRANSITION, + PHP_V3, + CPP_V2_TRANSITION, + CPP_V3, + GO_V3_TRANSITION, + GO_V4, + NET_V3_TRANSITION, + NET_V4 + ); + public static final Set TRANSITION_VERSIONS = Set.of( JAVA_V3_TRANSITION, From b9780376f49ca8bd5b4ae4196a025744472a04f4 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Mon, 15 Jun 2026 11:59:36 -0700 Subject: [PATCH 3/6] chore: sync .github/ workflows with public repo --- .github/workflows/all-ci.yml | 44 +++++++++++++++--------------- .github/workflows/python-integ.yml | 10 +++---- .github/workflows/python-perf.yml | 10 +++---- .github/workflows/test-server.yml | 10 +++---- 4 files changed, 37 insertions(+), 37 deletions(-) diff --git a/.github/workflows/all-ci.yml b/.github/workflows/all-ci.yml index 85332d55..d8c92bb9 100644 --- a/.github/workflows/all-ci.yml +++ b/.github/workflows/all-ci.yml @@ -2,7 +2,7 @@ name: All CI on: push: - # branches: [ main, staging ] + branches: [ main, staging ] pull_request: workflow_dispatch: inputs: @@ -13,9 +13,9 @@ on: type: string jobs: - # python-lint: - # name: Lint - # uses: ./.github/workflows/lint.yml + python-lint: + name: Lint + uses: ./.github/workflows/lint.yml run-test-server: permissions: @@ -27,25 +27,25 @@ jobs: python-version: ${{ inputs.python-version || '3.11' }} secrets: inherit - # python-integ: - # permissions: - # id-token: write - # contents: read - # name: Python Integration Tests - # uses: ./.github/workflows/python-integ.yml - # with: - # python-version: ${{ inputs.python-version || '3.11' }} - # secrets: inherit + python-integ: + permissions: + id-token: write + contents: read + name: Python Integration Tests + uses: ./.github/workflows/python-integ.yml + with: + python-version: ${{ inputs.python-version || '3.11' }} + secrets: inherit - # python-perf: - # permissions: - # id-token: write - # contents: read - # name: Python Performance Tests - # uses: ./.github/workflows/python-perf.yml - # with: - # python-version: ${{ inputs.python-version || '3.11' }} - # secrets: inherit + python-perf: + permissions: + id-token: write + contents: read + name: Python Performance Tests + uses: ./.github/workflows/python-perf.yml + with: + python-version: ${{ inputs.python-version || '3.11' }} + secrets: inherit run-duvet: permissions: diff --git a/.github/workflows/python-integ.yml b/.github/workflows/python-integ.yml index 475f0136..7c22d3e4 100644 --- a/.github/workflows/python-integ.yml +++ b/.github/workflows/python-integ.yml @@ -48,12 +48,12 @@ jobs: - name: Install dependencies run: make install - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v2 + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v6 with: - role-to-assume: arn:aws:iam::${{ secrets.CI_AWS_ACCOUNT_ID }}:role/service-role/${{ vars.CI_AWS_ROLE }} - role-session-name: S3EC-Github-CI-Tests - aws-region: ${{ vars.CI_AWS_REGION }} + special-characters-workaround: "true" + role-to-assume: arn:aws:iam::370957321024:role/S3EC-Python-Github-test-role + aws-region: us-west-2 - name: Run unit tests run: uv run pytest test/ --ignore=test/integration/ --ignore=test/performance/ --verbose --cov=src/s3_encryption --cov-report=term-missing --cov-report=html:coverage-unit --cov-fail-under=89 diff --git a/.github/workflows/python-perf.yml b/.github/workflows/python-perf.yml index 8752dc79..38bddb56 100644 --- a/.github/workflows/python-perf.yml +++ b/.github/workflows/python-perf.yml @@ -41,12 +41,12 @@ jobs: - name: Install dependencies run: make install - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v2 + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v6 with: - role-to-assume: arn:aws:iam::${{ secrets.CI_AWS_ACCOUNT_ID }}:role/service-role/${{ vars.CI_AWS_ROLE }} - role-session-name: S3EC-Github-CI-Tests - aws-region: ${{ vars.CI_AWS_REGION }} + special-characters-workaround: "true" + role-to-assume: arn:aws:iam::370957321024:role/S3EC-Python-Github-test-role + aws-region: us-west-2 - name: Run performance tests run: make test-perf diff --git a/.github/workflows/test-server.yml b/.github/workflows/test-server.yml index 58800464..b60c2167 100644 --- a/.github/workflows/test-server.yml +++ b/.github/workflows/test-server.yml @@ -99,12 +99,12 @@ jobs: restore-keys: | ${{ runner.os }}-gradle- - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v2 + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v6 with: - role-to-assume: arn:aws:iam::${{ secrets.CI_AWS_ACCOUNT_ID }}:role/service-role/${{ vars.CI_AWS_ROLE }} - role-session-name: S3EC-Github-CI-Tests - aws-region: ${{ vars.CI_AWS_REGION }} + special-characters-workaround: "true" + role-to-assume: arn:aws:iam::370957321024:role/S3EC-Python-Github-test-role + aws-region: us-west-2 - name: Build the servers run: cd test-server && make build-all-servers From c8f96d2c7d32c758b3c8d051f5e2297990b42cd1 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Mon, 15 Jun 2026 12:08:34 -0700 Subject: [PATCH 4/6] m --- .gitmodules | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitmodules b/.gitmodules index a2d0d7d5..3b91bba5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -28,16 +28,16 @@ branch = fire-egg-staging [submodule "test-server/net-v4-server/s3ec-net-v4-improved"] path = test-server/net-v4-server/s3ec-net-v4-improved - url = https://github.com/aws/private-amazon-s3-encryption-client-dotnet-staging.git - branch = main-fix-Bleichenbacher + url = https://github.com/rishav-karanjit/amazon-s3-encryption-client-dotnet.git + branch = s3ecv4-fix [submodule "test-server/go-v3-transition-server/local-go-s3ec"] path = test-server/go-v3-transition-server/local-go-s3ec url = https://github.com/aws/amazon-s3-encryption-client-go branch = main [submodule "test-server/net-v3-transition-server/s3ec-v3-transition-branch"] path = test-server/net-v3-transition-server/s3ec-v3-transition-branch - url = https://github.com/aws/private-amazon-s3-encryption-client-dotnet-staging.git - branch = v4sdk-fix-Bleichenbacher + url = https://github.com/rishav-karanjit/amazon-s3-encryption-client-dotnet.git + branch = v4sdk-fix-Bleichenbacher-for-public [submodule "test-server/cpp-v2-transition-server/aws-sdk-cpp"] path = test-server/cpp-v2-transition-server/aws-sdk-cpp url = git@github.com:aws/aws-sdk-cpp.git From e79de21a59fdbb503d76f3f8adf33b465ad535a8 Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Mon, 15 Jun 2026 12:15:40 -0700 Subject: [PATCH 5/6] chore: point .NET submodules to public fork branches --- test-server/net-v3-transition-server/s3ec-v3-transition-branch | 2 +- test-server/net-v4-server/s3ec-net-v4-improved | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test-server/net-v3-transition-server/s3ec-v3-transition-branch b/test-server/net-v3-transition-server/s3ec-v3-transition-branch index fe9de0ee..903d3514 160000 --- a/test-server/net-v3-transition-server/s3ec-v3-transition-branch +++ b/test-server/net-v3-transition-server/s3ec-v3-transition-branch @@ -1 +1 @@ -Subproject commit fe9de0eee5c819075098e6de79065f5a722784b4 +Subproject commit 903d3514cf759f35508ceff011bfa423bdc89efb diff --git a/test-server/net-v4-server/s3ec-net-v4-improved b/test-server/net-v4-server/s3ec-net-v4-improved index f3920f60..c736f09d 160000 --- a/test-server/net-v4-server/s3ec-net-v4-improved +++ b/test-server/net-v4-server/s3ec-net-v4-improved @@ -1 +1 @@ -Subproject commit f3920f609583e1c24613efbf4efa37b40e6336f5 +Subproject commit c736f09df954877149c3067ef07865cbcdef22f0 From b3bc8a823253ae33c478d4c3caf97db873a3e43a Mon Sep 17 00:00:00 2001 From: rishav-karanjit Date: Wed, 17 Jun 2026 12:11:15 -0700 Subject: [PATCH 6/6] m --- .gitmodules | 8 ++++---- .../net-v3-transition-server/s3ec-v3-transition-branch | 2 +- test-server/net-v4-server/s3ec-net-v4-improved | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.gitmodules b/.gitmodules index 3b91bba5..1e3a2922 100644 --- a/.gitmodules +++ b/.gitmodules @@ -28,16 +28,16 @@ branch = fire-egg-staging [submodule "test-server/net-v4-server/s3ec-net-v4-improved"] path = test-server/net-v4-server/s3ec-net-v4-improved - url = https://github.com/rishav-karanjit/amazon-s3-encryption-client-dotnet.git - branch = s3ecv4-fix + url = https://github.com/aws/amazon-s3-encryption-client-dotnet.git + branch = dev [submodule "test-server/go-v3-transition-server/local-go-s3ec"] path = test-server/go-v3-transition-server/local-go-s3ec url = https://github.com/aws/amazon-s3-encryption-client-go branch = main [submodule "test-server/net-v3-transition-server/s3ec-v3-transition-branch"] path = test-server/net-v3-transition-server/s3ec-v3-transition-branch - url = https://github.com/rishav-karanjit/amazon-s3-encryption-client-dotnet.git - branch = v4sdk-fix-Bleichenbacher-for-public + url = https://github.com/aws/amazon-s3-encryption-client-dotnet.git + branch = v4sdk-development [submodule "test-server/cpp-v2-transition-server/aws-sdk-cpp"] path = test-server/cpp-v2-transition-server/aws-sdk-cpp url = git@github.com:aws/aws-sdk-cpp.git diff --git a/test-server/net-v3-transition-server/s3ec-v3-transition-branch b/test-server/net-v3-transition-server/s3ec-v3-transition-branch index 903d3514..c2c19c7f 160000 --- a/test-server/net-v3-transition-server/s3ec-v3-transition-branch +++ b/test-server/net-v3-transition-server/s3ec-v3-transition-branch @@ -1 +1 @@ -Subproject commit 903d3514cf759f35508ceff011bfa423bdc89efb +Subproject commit c2c19c7f09b6dfb5eb4f9ca7a42da443a0c54f08 diff --git a/test-server/net-v4-server/s3ec-net-v4-improved b/test-server/net-v4-server/s3ec-net-v4-improved index c736f09d..2cc436f4 160000 --- a/test-server/net-v4-server/s3ec-net-v4-improved +++ b/test-server/net-v4-server/s3ec-net-v4-improved @@ -1 +1 @@ -Subproject commit c736f09df954877149c3067ef07865cbcdef22f0 +Subproject commit 2cc436f49a5a07c220c6a627da1b9831a3354836