|
15 | 15 | import java.nio.charset.StandardCharsets; |
16 | 16 | import java.security.KeyPair; |
17 | 17 | import java.security.KeyPairGenerator; |
| 18 | +import java.util.Base64; |
18 | 19 | import java.util.HashMap; |
19 | 20 | import java.util.List; |
20 | 21 | import java.util.Map; |
| 22 | +import java.util.Random; |
21 | 23 |
|
22 | 24 | import com.amazonaws.services.s3.AmazonS3EncryptionClientV2; |
23 | 25 | import com.amazonaws.services.s3.AmazonS3EncryptionV2; |
|
28 | 30 | import org.junit.jupiter.params.provider.MethodSource; |
29 | 31 | import org.opentest4j.TestAbortedException; |
30 | 32 | import software.amazon.awssdk.core.ResponseBytes; |
| 33 | +import software.amazon.awssdk.core.sync.RequestBody; |
31 | 34 | import software.amazon.awssdk.services.s3.S3Client; |
32 | 35 | import software.amazon.awssdk.services.s3.model.GetObjectResponse; |
33 | 36 | import software.amazon.encryption.s3.TestUtils.LanguageServerTarget; |
@@ -59,6 +62,87 @@ public static void setup() { |
59 | 62 | validateServersRunning(); |
60 | 63 | } |
61 | 64 |
|
| 65 | + /** |
| 66 | + * Verifies input validation via fuzzing of commitment parameters. |
| 67 | + * - Iterates multiple times with random garbage in critical metadata fields. |
| 68 | + * - Ensures client handles invalid input gracefully (throws exception, doesn't |
| 69 | + * crash). |
| 70 | + */ |
| 71 | + @ParameterizedTest(name = "{displayName}: Fuzzing {0}") |
| 72 | + @MethodSource("software.amazon.encryption.s3.TestUtils#improvedClientsForTest") |
| 73 | + public void testCommitmentParameterFuzzing(TestUtils.LanguageServerTarget target) throws Exception { |
| 74 | + S3ECTestServerClient testClient = TestUtils.testServerClientFor(target); |
| 75 | + final String objectKey = TestUtils.appendTestSuffix("fuzzing-" + target); |
| 76 | + final String testInput = "fuzzing metadata"; |
| 77 | + KeyMaterial kmsKeyArn = KeyMaterial.builder().kmsKeyId(TestUtils.KMS_KEY_ARN).build(); |
| 78 | + |
| 79 | + // 1. Create Valid Object |
| 80 | + String clientId = testClient.createClient(CreateClientInput.builder() |
| 81 | + .config(S3ECConfig.builder() |
| 82 | + .keyMaterial(kmsKeyArn) |
| 83 | + .commitmentPolicy(CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT) |
| 84 | + .build()) |
| 85 | + .build()).getClientId(); |
| 86 | + |
| 87 | + testClient.putObject(PutObjectInput.builder() |
| 88 | + .clientID(clientId) |
| 89 | + .bucket(TestUtils.BUCKET) |
| 90 | + .key(objectKey) |
| 91 | + .body(ByteBuffer.wrap(testInput.getBytes(StandardCharsets.UTF_8))) |
| 92 | + .build()); |
| 93 | + |
| 94 | + // 2. Get Original Metadata |
| 95 | + S3Client s3Client = S3Client.create(); |
| 96 | + ResponseBytes<GetObjectResponse> objectBytes = s3Client |
| 97 | + .getObjectAsBytes(b -> b.bucket(TestUtils.BUCKET).key(objectKey)); |
| 98 | + Map<String, String> originalMetadata = objectBytes.response().metadata(); |
| 99 | + |
| 100 | + // 3. Fuzzing Loop |
| 101 | + Random random = new Random(); |
| 102 | + String[] fieldsToFuzz = { "x-amz-c", "x-amz-i", "x-amz-3" }; |
| 103 | + |
| 104 | + for (int i = 0; i < 20; i++) { |
| 105 | + String field = fieldsToFuzz[random.nextInt(fieldsToFuzz.length)]; |
| 106 | + Map<String, String> fuzzedMetadata = new HashMap<>(originalMetadata); |
| 107 | + |
| 108 | + // Generate random garbage |
| 109 | + byte[] garbage = new byte[random.nextInt(50) + 1]; |
| 110 | + random.nextBytes(garbage); |
| 111 | + String garbageStr = Base64.getEncoder().encodeToString(garbage); |
| 112 | + |
| 113 | + // Occasionally inject non-Base64 garbage |
| 114 | + if (random.nextBoolean()) { |
| 115 | + garbageStr = "INVALID_BASE64_!@#$%^&*()"; |
| 116 | + } |
| 117 | + |
| 118 | + fuzzedMetadata.put(field, garbageStr); |
| 119 | + |
| 120 | + // Upload with fuzzed metadata |
| 121 | + s3Client.putObject(software.amazon.awssdk.services.s3.model.PutObjectRequest.builder() |
| 122 | + .bucket(TestUtils.BUCKET) |
| 123 | + .key(objectKey) // Overwrite |
| 124 | + .metadata(fuzzedMetadata) |
| 125 | + .build(), |
| 126 | + RequestBody.fromBytes(objectBytes.asByteArray())); |
| 127 | + |
| 128 | + // Attempt Decryption |
| 129 | + try { |
| 130 | + testClient.getObject(GetObjectInput.builder() |
| 131 | + .clientID(clientId) |
| 132 | + .bucket(TestUtils.BUCKET) |
| 133 | + .key(objectKey) |
| 134 | + .build()); |
| 135 | + fail("Should fail with fuzzed metadata field: " + field); |
| 136 | + } catch (S3EncryptionClientError e) { |
| 137 | + // Expected |
| 138 | + } |
| 139 | + } |
| 140 | + |
| 141 | + // cleanup |
| 142 | + s3Client.close(); |
| 143 | + } |
| 144 | + |
| 145 | + |
62 | 146 | @ParameterizedTest(name = "{displayName} for Encrypt: {0}, Decrypt: {1}") |
63 | 147 | @MethodSource("software.amazon.encryption.s3.TestUtils#crossLanguageClients") |
64 | 148 | public void crossLanguageTestKms(LanguageServerTarget encLang, LanguageServerTarget decLang) { |
|
0 commit comments