Skip to content

Commit 0fed900

Browse files
authored
chore: add metadata downgrade tests(#55)
1 parent c6b4e64 commit 0fed900

File tree

1 file changed

+187
-0
lines changed

1 file changed

+187
-0
lines changed
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
package software.amazon.encryption.s3;
2+
3+
import org.junit.jupiter.api.BeforeAll;
4+
import org.junit.jupiter.api.Test;
5+
import software.amazon.awssdk.core.ResponseInputStream;
6+
import software.amazon.awssdk.core.sync.RequestBody;
7+
import software.amazon.awssdk.services.s3.S3Client;
8+
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
9+
10+
import javax.crypto.KeyGenerator;
11+
import javax.crypto.SecretKey;
12+
import java.security.NoSuchAlgorithmException;
13+
import java.util.HashMap;
14+
import java.util.Map;
15+
16+
import static org.junit.jupiter.api.Assertions.assertThrows;
17+
import static software.amazon.encryption.s3.utils.S3EncryptionClientTestResources.BUCKET;
18+
import static software.amazon.encryption.s3.utils.S3EncryptionClientTestResources.deleteObject;
19+
20+
public class ParameterMalleabilityTest {
21+
22+
private static SecretKey AES_KEY;
23+
24+
@BeforeAll
25+
public static void setUp() throws NoSuchAlgorithmException {
26+
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
27+
keyGen.init(256);
28+
AES_KEY = keyGen.generateKey();
29+
}
30+
31+
@Test
32+
public void contentEncryptionDowngradeAttackFails() {
33+
final String objectKey = "content-downgrade-attack-fails";
34+
S3Client v3Client = S3EncryptionClient.builder()
35+
.aesKey(AES_KEY)
36+
.build();
37+
final String input = "ContentDowngradeAttackFails";
38+
39+
// Encrypt something using AES-GCM
40+
v3Client.putObject(builder -> builder.bucket(BUCKET).key(objectKey), RequestBody.fromString(input));
41+
42+
// Using a default client, tamper with the metadata
43+
// CBC mode uses no parameter value, so just remove the "cek-alg" key
44+
S3Client defaultClient = S3Client.builder().build();
45+
ResponseInputStream<GetObjectResponse> response = defaultClient.getObject(builder -> builder.bucket(BUCKET).key(objectKey));
46+
final Map<String, String> objectMetadata = response.response().metadata();
47+
final Map<String, String> tamperedMetadata = new HashMap<>(objectMetadata);
48+
tamperedMetadata.remove("x-amz-cek-alg");
49+
50+
// Replace the object with the content encryption algorithm removed
51+
defaultClient.putObject(builder -> builder.bucket(BUCKET).key(objectKey).metadata(tamperedMetadata),
52+
RequestBody.fromInputStream(response, response.response().contentLength()));
53+
54+
// getObject fails
55+
assertThrows(Exception.class, () -> v3Client.getObject(builder -> builder.bucket(BUCKET).key(objectKey)));
56+
57+
// Enabling unauthenticated modes also fail
58+
S3Client v3ClientUnauthenticated = S3EncryptionClient.builder()
59+
.aesKey(AES_KEY)
60+
.enableLegacyUnauthenticatedModes(true)
61+
.enableLegacyWrappingAlgorithms(true)
62+
.build();
63+
assertThrows(Exception.class, () -> v3ClientUnauthenticated.getObject(builder -> builder.bucket(BUCKET).key(objectKey)));
64+
65+
// Cleanup
66+
deleteObject(BUCKET, objectKey, v3Client);
67+
v3Client.close();
68+
}
69+
70+
@Test
71+
public void keyWrapRemovalAttackFails() {
72+
final String objectKey = "keywrap-removal-attack-fails";
73+
S3Client v3Client = S3EncryptionClient.builder()
74+
.aesKey(AES_KEY)
75+
.build();
76+
final String input = "KeyWrapRemovalAttackFails";
77+
78+
// Encrypt something using AES-GCM
79+
v3Client.putObject(builder -> builder.bucket(BUCKET).key(objectKey), RequestBody.fromString(input));
80+
81+
// Using a default client, tamper with the metadata
82+
S3Client defaultClient = S3Client.builder().build();
83+
ResponseInputStream<GetObjectResponse> response = defaultClient.getObject(builder -> builder.bucket(BUCKET).key(objectKey));
84+
final Map<String, String> objectMetadata = response.response().metadata();
85+
final Map<String, String> tamperedMetadata = new HashMap<>(objectMetadata);
86+
tamperedMetadata.remove("x-amz-wrap-alg");
87+
88+
// Replace the object
89+
defaultClient.putObject(builder -> builder.bucket(BUCKET).key(objectKey).metadata(tamperedMetadata),
90+
RequestBody.fromInputStream(response, response.response().contentLength()));
91+
92+
// getObject fails
93+
assertThrows(Exception.class, () -> v3Client.getObject(builder -> builder.bucket(BUCKET).key(objectKey)));
94+
95+
// Enabling unauthenticated modes also fail
96+
S3Client v3ClientUnauthenticated = S3EncryptionClient.builder()
97+
.aesKey(AES_KEY)
98+
.enableLegacyUnauthenticatedModes(true)
99+
.enableLegacyWrappingAlgorithms(true)
100+
.build();
101+
assertThrows(Exception.class, () -> v3ClientUnauthenticated.getObject(builder -> builder.bucket(BUCKET).key(objectKey)));
102+
103+
// Cleanup
104+
deleteObject(BUCKET, objectKey, v3Client);
105+
v3Client.close();
106+
}
107+
108+
@Test
109+
public void keyWrapDowngradeAesWrapAttackFails() {
110+
final String objectKey = "keywrap-downgrade-aeswrap-attack-fails";
111+
S3Client v3Client = S3EncryptionClient.builder()
112+
.aesKey(AES_KEY)
113+
.build();
114+
final String input = "KeyWrapDowngradeAesWrapAttackFails";
115+
116+
// Encrypt something using AES-GCM
117+
v3Client.putObject(builder -> builder.bucket(BUCKET).key(objectKey), RequestBody.fromString(input));
118+
119+
// Using a default client, tamper with the metadata
120+
S3Client defaultClient = S3Client.builder().build();
121+
ResponseInputStream<GetObjectResponse> response = defaultClient.getObject(builder -> builder.bucket(BUCKET).key(objectKey));
122+
final Map<String, String> objectMetadata = response.response().metadata();
123+
final Map<String, String> tamperedMetadata = new HashMap<>(objectMetadata);
124+
// Replace wrap-alg with AESWrap
125+
tamperedMetadata.put("x-amz-wrap-alg", "AESWrap");
126+
127+
// Replace the object
128+
defaultClient.putObject(builder -> builder.bucket(BUCKET).key(objectKey).metadata(tamperedMetadata),
129+
RequestBody.fromInputStream(response, response.response().contentLength()));
130+
131+
// getObject fails
132+
assertThrows(Exception.class, () -> v3Client.getObject(builder -> builder.bucket(BUCKET).key(objectKey)));
133+
134+
// Enabling unauthenticated modes also fail
135+
S3Client v3ClientUnauthenticated = S3EncryptionClient.builder()
136+
.enableLegacyWrappingAlgorithms(true)
137+
.enableLegacyUnauthenticatedModes(true)
138+
.aesKey(AES_KEY)
139+
.build();
140+
assertThrows(Exception.class, () -> v3ClientUnauthenticated.getObject(builder -> builder.bucket(BUCKET).key(objectKey)));
141+
142+
// Cleanup
143+
deleteObject(BUCKET, objectKey, v3Client);
144+
v3Client.close();
145+
}
146+
147+
@Test
148+
public void keyWrapDowngradeAesAttackFails() {
149+
final String objectKey = "keywrap-downgrade-aes-attack-fails";
150+
S3Client v3Client = S3EncryptionClient.builder()
151+
.aesKey(AES_KEY)
152+
.build();
153+
final String input = "KeyWrapDowngradeAesAttackFails";
154+
155+
// Encrypt something using AES-GCM
156+
v3Client.putObject(builder -> builder.bucket(BUCKET).key(objectKey), RequestBody.fromString(input));
157+
158+
// Using a default client, tamper with the metadata
159+
S3Client defaultClient = S3Client.builder().build();
160+
ResponseInputStream<GetObjectResponse> response = defaultClient.getObject(builder -> builder.bucket(BUCKET).key(objectKey));
161+
final Map<String, String> objectMetadata = response.response().metadata();
162+
final Map<String, String> tamperedMetadata = new HashMap<>(objectMetadata);
163+
// Replace wrap-alg with AES
164+
tamperedMetadata.put("x-amz-wrap-alg", "AES");
165+
166+
// Replace the object
167+
defaultClient.putObject(builder -> builder.bucket(BUCKET).key(objectKey).metadata(tamperedMetadata),
168+
RequestBody.fromInputStream(response, response.response().contentLength()));
169+
170+
// getObject fails
171+
assertThrows(Exception.class, () -> v3Client.getObject(builder -> builder.bucket(BUCKET).key(objectKey)));
172+
173+
// Enabling unauthenticated modes also fail
174+
S3Client v3ClientUnauthenticated = S3EncryptionClient.builder()
175+
.aesKey(AES_KEY)
176+
.enableLegacyWrappingAlgorithms(true)
177+
.enableLegacyUnauthenticatedModes(true)
178+
.build();
179+
assertThrows(Exception.class, () -> v3ClientUnauthenticated.getObject(builder -> builder.bucket(BUCKET).key(objectKey)));
180+
181+
// Cleanup
182+
deleteObject(BUCKET, objectKey, v3Client);
183+
v3Client.close();
184+
}
185+
186+
187+
}

0 commit comments

Comments
 (0)