Skip to content

Commit fd64f1c

Browse files
committed
cleanup
1 parent 898c55e commit fd64f1c

File tree

8 files changed

+127
-10
lines changed

8 files changed

+127
-10
lines changed

src/main/java/software/amazon/encryption/s3/S3EncryptionClient.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ public PutObjectResponse putObject(PutObjectRequest putObjectRequest, RequestBod
205205
.s3AsyncClient(_wrappedAsyncClient)
206206
.cryptoMaterialsManager(_cryptoMaterialsManager)
207207
.secureRandom(_secureRandom)
208+
.instructionFileConfig(_instructionFileConfig)
208209
.build();
209210

210211
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

src/main/java/software/amazon/encryption/s3/internal/ContentMetadataDecodingStrategy.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,6 @@ private ContentMetadata readFromMap(Map<String, String> metadata, GetObjectRespo
168168

169169
public ContentMetadata decode(GetObjectRequest request, GetObjectResponse response) {
170170
Map<String, String> metadata = response.metadata();
171-
ContentMetadataDecodingStrategy strategy;
172171
if (metadata != null
173172
&& metadata.containsKey(MetadataKeyConstants.CONTENT_IV)
174173
&& (metadata.containsKey(MetadataKeyConstants.ENCRYPTED_DATA_KEY_V1)

src/main/java/software/amazon/encryption/s3/internal/ContentMetadataEncodingStrategy.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,9 @@ public ContentMetadataEncodingStrategy(InstructionFileConfig instructionFileConf
2323

2424
public PutObjectRequest encodeMetadata(EncryptionMaterials materials, byte[] iv, PutObjectRequest putObjectRequest) {
2525
if (_instructionFileConfig.isInstructionFilePutEnabled()) {
26-
// TODO: serialize inst file as string
2726
final String metadataString = metadataToString(materials, iv);
28-
_instructionFileConfig.putInstructionFile(putObjectRequest, "");
29-
// the original object is returned as-is
27+
_instructionFileConfig.putInstructionFile(putObjectRequest, metadataString);
28+
// the original request object is returned as-is
3029
return putObjectRequest;
3130
} else {
3231
Map<String, String> newMetadata = addMetadataToMap(putObjectRequest.metadata(), materials, iv);
@@ -45,7 +44,20 @@ public CreateMultipartUploadRequest encodeMetadata(EncryptionMaterials materials
4544

4645
private String metadataToString(EncryptionMaterials materials, byte[] iv) {
4746
// this is just the metadata map serialized as JSON
48-
return "";
47+
// so first get the Map
48+
final Map<String, String> metadataMap = addMetadataToMap(new HashMap<>(), materials, iv);
49+
// then serialize it
50+
try (JsonWriter jsonWriter = JsonWriter.create()) {
51+
jsonWriter.writeStartObject();
52+
for (Map.Entry<String, String> entry : metadataMap.entrySet()) {
53+
jsonWriter.writeFieldName(entry.getKey()).writeValue(entry.getValue());
54+
}
55+
jsonWriter.writeEndObject();
56+
57+
return new String(jsonWriter.getBytes(), StandardCharsets.UTF_8);
58+
} catch (JsonWriter.JsonGenerationException e) {
59+
throw new S3EncryptionClientException("Cannot serialize materials to JSON.", e);
60+
}
4961
}
5062

5163
private Map<String, String> addMetadataToMap(Map<String, String> map, EncryptionMaterials materials, byte[] iv) {

src/main/java/software/amazon/encryption/s3/internal/InstructionFileConfig.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@
1212
import software.amazon.awssdk.services.s3.model.PutObjectResponse;
1313
import software.amazon.encryption.s3.S3EncryptionClientException;
1414

15+
import java.util.HashMap;
16+
import java.util.Map;
17+
18+
import static software.amazon.encryption.s3.S3EncryptionClientUtilities.INSTRUCTION_FILE_SUFFIX;
19+
import static software.amazon.encryption.s3.internal.MetadataKeyConstants.INSTRUCTION_FILE;
20+
1521
/**
1622
* Provides configuration options for instruction file behaviors.
1723
*/
@@ -49,11 +55,23 @@ PutObjectResponse putInstructionFile(PutObjectRequest request, String instructio
4955
if (!_enableInstructionFilePut) {
5056
throw new S3EncryptionClientException("Enable Instruction File Put must be set to true in order to call PutObject with an instruction file!");
5157
}
58+
59+
// Instruction file DOES NOT contain the same metadata as the actual object
60+
Map<String, String> instFileMetadata = new HashMap<>(1);
61+
// It contains a key with no value identifying it as an instruction file
62+
instFileMetadata.put(INSTRUCTION_FILE, "");
63+
64+
// In a future release, non-default suffixes will be supported.
65+
// Use toBuilder to keep all other fields the same as the actual request
66+
final PutObjectRequest instPutRequest = request.toBuilder()
67+
.key(request.key() + INSTRUCTION_FILE_SUFFIX)
68+
.metadata(instFileMetadata)
69+
.build();
5270
switch (_clientType) {
5371
case SYNCHRONOUS:
54-
return _s3Client.putObject(request, RequestBody.fromString(instructionFileContent));
72+
return _s3Client.putObject(instPutRequest, RequestBody.fromString(instructionFileContent));
5573
case ASYNC:
56-
return _s3AsyncClient.putObject(request, AsyncRequestBody.fromString(instructionFileContent)).join();
74+
return _s3AsyncClient.putObject(instPutRequest, AsyncRequestBody.fromString(instructionFileContent)).join();
5775
case DISABLED:
5876
// this should never happen because we check enablePut first
5977
throw new S3EncryptionClientException("Instruction File has been disabled!");

src/main/java/software/amazon/encryption/s3/internal/MetadataKeyConstants.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,6 @@ public class MetadataKeyConstants {
1313
// This is usually an actual Java cipher e.g. AES/GCM/NoPadding
1414
public static final String CONTENT_CIPHER = "x-amz-cek-alg";
1515
public static final String CONTENT_CIPHER_TAG_LENGTH = "x-amz-tag-len";
16+
// Only used in instruction files to identify them as such
17+
public static final String INSTRUCTION_FILE = "x-amz-crypto-instr-file";
1618
}

src/main/java/software/amazon/encryption/s3/internal/MultipartUploadObjectPipeline.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,12 +216,13 @@ public void putLocalObject(RequestBody requestBody, String uploadId, OutputStrea
216216
public static class Builder {
217217
private final Map<String, MultipartUploadMaterials> _multipartUploadMaterials =
218218
Collections.synchronizedMap(new HashMap<>());
219-
private final ContentMetadataEncodingStrategy _contentMetadataEncodingStrategy = new ContentMetadataEncodingStrategy();
219+
private ContentMetadataEncodingStrategy _contentMetadataEncodingStrategy;
220220
private S3AsyncClient _s3AsyncClient;
221221
private CryptographicMaterialsManager _cryptoMaterialsManager;
222222
private SecureRandom _secureRandom;
223223
// To Create Cipher which is used in during uploadPart requests.
224224
private MultipartContentEncryptionStrategy _contentEncryptionStrategy;
225+
private InstructionFileConfig _instructionFileConfig;
225226

226227
private Builder() {
227228
}
@@ -246,6 +247,11 @@ public Builder secureRandom(SecureRandom secureRandom) {
246247
return this;
247248
}
248249

250+
public Builder instructionFileConfig(InstructionFileConfig instructionFileConfig) {
251+
this._instructionFileConfig = instructionFileConfig;
252+
return this;
253+
}
254+
249255
public MultipartUploadObjectPipeline build() {
250256
// Default to AesGcm since it is the only active (non-legacy) content encryption strategy
251257
if (_contentEncryptionStrategy == null) {
@@ -254,6 +260,7 @@ public MultipartUploadObjectPipeline build() {
254260
.secureRandom(_secureRandom)
255261
.build();
256262
}
263+
_contentMetadataEncodingStrategy = new ContentMetadataEncodingStrategy(_instructionFileConfig);
257264
return new MultipartUploadObjectPipeline(this);
258265
}
259266
}

src/test/java/software/amazon/encryption/s3/S3EncryptionClientCompatibilityTest.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,7 @@ public void KmsV1toV3() {
561561

562562
CryptoConfiguration v1Config =
563563
new CryptoConfiguration(CryptoMode.AuthenticatedEncryption)
564+
.withStorageMode(CryptoStorageMode.InstructionFile)
564565
.withAwsKmsRegion(KMS_REGION);
565566

566567
AmazonS3Encryption v1Client = AmazonS3EncryptionClient.encryptionBuilder()
@@ -585,7 +586,7 @@ public void KmsV1toV3() {
585586
assertEquals(input, output);
586587

587588
// Cleanup
588-
deleteObject(BUCKET, objectKey, v3Client);
589+
// deleteObject(BUCKET, objectKey, v3Client);
589590
v3Client.close();
590591
}
591592

@@ -596,8 +597,10 @@ public void KmsContextV2toV3() {
596597
// V2 Client
597598
EncryptionMaterialsProvider materialsProvider = new KMSEncryptionMaterialsProvider(KMS_KEY_ID);
598599

600+
CryptoConfigurationV2 config = new CryptoConfigurationV2(CryptoMode.StrictAuthenticatedEncryption);
599601
AmazonS3EncryptionV2 v2Client = AmazonS3EncryptionClientV2.encryptionBuilder()
600602
.withEncryptionMaterialsProvider(materialsProvider)
603+
.withCryptoConfiguration(config)
601604
.build();
602605

603606
// V3 Client

src/test/java/software/amazon/encryption/s3/S3EncryptionClientTest.java

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1102,6 +1102,81 @@ public void testInstructionFileConfig() {
11021102
s3Client.close();
11031103
}
11041104

1105+
@Test
1106+
public void testPutWithInstructionFile() {
1107+
final String objectKey = appendTestSuffix("instruction-file-put-object");
1108+
final String objectKeyV2 = appendTestSuffix("instruction-file-put-object-v2");
1109+
final String input = "SimpleTestOfV3EncryptionClient";
1110+
S3Client wrappedClient = S3Client.create();
1111+
S3Client s3Client = S3EncryptionClient.builder()
1112+
.instructionFileConfig(InstructionFileConfig.builder()
1113+
.instructionFileClient(wrappedClient)
1114+
.enableInstructionFilePutObject(true)
1115+
.build())
1116+
.kmsKeyId(KMS_KEY_ID)
1117+
.build();
1118+
1119+
s3Client.putObject(builder -> builder
1120+
.bucket(BUCKET)
1121+
.key(objectKey)
1122+
.build(), RequestBody.fromString(input));
1123+
1124+
// Disabled client should fail
1125+
S3Client s3ClientDisabledInstructionFile = S3EncryptionClient.builder()
1126+
.wrappedClient(wrappedClient)
1127+
.instructionFileConfig(InstructionFileConfig.builder()
1128+
.disableInstructionFile(true)
1129+
.build())
1130+
.kmsKeyId(KMS_KEY_ID)
1131+
.build();
1132+
1133+
try {
1134+
s3ClientDisabledInstructionFile.getObjectAsBytes(builder -> builder
1135+
.bucket(BUCKET)
1136+
.key(objectKey)
1137+
.build());
1138+
fail("expected exception");
1139+
} catch (S3EncryptionClientException exception) {
1140+
assertTrue(exception.getMessage().contains("Exception encountered while fetching Instruction File."));
1141+
}
1142+
1143+
// Get the instruction file separately using a default client
1144+
S3Client defaultClient = S3Client.create();
1145+
ResponseBytes<GetObjectResponse> directInstGetResponse = defaultClient.getObjectAsBytes(builder -> builder
1146+
.bucket(BUCKET)
1147+
.key(objectKey + ".instruction")
1148+
.build());
1149+
assertTrue(directInstGetResponse.response().metadata().containsKey("x-amz-crypto-instr-file"));
1150+
1151+
ResponseBytes<GetObjectResponse> objectResponse = s3Client.getObjectAsBytes(builder -> builder
1152+
.bucket(BUCKET)
1153+
.key(objectKey)
1154+
.build());
1155+
String output = objectResponse.asUtf8String();
1156+
assertEquals(input, output);
1157+
1158+
1159+
// Temporary - Generate an instruction file in V2 to compare against V3
1160+
// TODO: do this for other keyrings as well
1161+
EncryptionMaterialsProvider materialsProvider =
1162+
new StaticEncryptionMaterialsProvider(new KMSEncryptionMaterials(KMS_KEY_ID));
1163+
CryptoConfigurationV2 cryptoConfig =
1164+
new CryptoConfigurationV2(CryptoMode.StrictAuthenticatedEncryption)
1165+
.withStorageMode(CryptoStorageMode.InstructionFile);
1166+
1167+
AmazonS3EncryptionV2 v2Client = AmazonS3EncryptionClientV2.encryptionBuilder()
1168+
.withCryptoConfiguration(cryptoConfig)
1169+
.withEncryptionMaterialsProvider(materialsProvider)
1170+
.build();
1171+
1172+
v2Client.putObject(BUCKET, objectKeyV2, input);
1173+
1174+
// Cleanup
1175+
// deleteObject(BUCKET, objectKey, s3Client);
1176+
s3ClientDisabledInstructionFile.close();
1177+
s3Client.close();
1178+
}
1179+
11051180
/**
11061181
* A simple, reusable round-trip (encryption + decryption) using a given
11071182
* S3Client. Useful for testing client configuration.
@@ -1124,4 +1199,4 @@ private void simpleV3RoundTrip(final S3Client v3Client, final String objectKey)
11241199
String output = objectResponse.asUtf8String();
11251200
assertEquals(input, output);
11261201
}
1127-
}
1202+
}

0 commit comments

Comments
 (0)