Skip to content

fix: allow CipherSubscriber to determine if the part is last part #453

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 31 commits into from
Apr 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>bom</artifactId>
<version>2.30.38</version>
<version>2.31.14</version>
<optional>true</optional>
<type>pom</type>
<scope>import</scope>
Expand All @@ -68,21 +68,21 @@
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId>
<version>2.30.38</version>
<version>2.31.14</version>
</dependency>

<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>kms</artifactId>
<version>2.30.38</version>
<version>2.31.14</version>
</dependency>

<!-- Used when enableMultipartPutObject is configured -->
<dependency>
<groupId>software.amazon.awssdk.crt</groupId>
<artifactId>aws-crt</artifactId>
<optional>true</optional>
<version>0.36.3</version>
<version>0.37.0</version>
</dependency>

<dependency>
Expand Down Expand Up @@ -163,7 +163,7 @@
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>sts</artifactId>
<version>2.30.38</version>
<version>2.31.14</version>
<optional>true</optional>
<scope>test</scope>
</dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
package software.amazon.encryption.s3.examples;

import static org.junit.jupiter.api.Assertions.assertTrue;
import static software.amazon.encryption.s3.S3EncryptionClient.withAdditionalConfiguration;
import static software.amazon.encryption.s3.utils.S3EncryptionClientTestResources.KMS_KEY_ID;
import static software.amazon.encryption.s3.utils.S3EncryptionClientTestResources.appendTestSuffix;

import org.apache.commons.io.IOUtils;
import software.amazon.awssdk.core.ResponseInputStream;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
Expand All @@ -26,7 +22,10 @@
import java.util.List;
import java.util.Map;

import org.apache.commons.io.IOUtils;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static software.amazon.encryption.s3.S3EncryptionClient.withAdditionalConfiguration;
import static software.amazon.encryption.s3.utils.S3EncryptionClientTestResources.KMS_KEY_ID;
import static software.amazon.encryption.s3.utils.S3EncryptionClientTestResources.appendTestSuffix;

public class MultipartUploadExample {
public static String BUCKET;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ public class CipherAsyncRequestBody implements AsyncRequestBody {
private final Long ciphertextLength;
private final CryptographicMaterials materials;
private final byte[] iv;
private final boolean isLastPart;

public CipherAsyncRequestBody(final AsyncRequestBody wrappedAsyncRequestBody, final Long ciphertextLength, final CryptographicMaterials materials, final byte[] iv, final boolean isLastPart) {
this.wrappedAsyncRequestBody = wrappedAsyncRequestBody;
this.ciphertextLength = ciphertextLength;
this.materials = materials;
this.iv = iv;
this.isLastPart = isLastPart;
}

public CipherAsyncRequestBody(final AsyncRequestBody wrappedAsyncRequestBody, final Long ciphertextLength, final CryptographicMaterials materials, final byte[] iv) {
Expand All @@ -38,7 +40,7 @@ public CipherAsyncRequestBody(final AsyncRequestBody wrappedAsyncRequestBody, fi
public void subscribe(Subscriber<? super ByteBuffer> subscriber) {
wrappedAsyncRequestBody.subscribe(new CipherSubscriber(subscriber,
contentLength().orElseThrow(() -> new S3EncryptionClientException("Unbounded streams are currently not supported.")),
materials, iv));
materials, iv, isLastPart));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,13 @@ public class CipherSubscriber implements Subscriber<ByteBuffer> {
private final Subscriber<? super ByteBuffer> wrappedSubscriber;
private Cipher cipher;
private final Long contentLength;
private final CryptographicMaterials materials;
private byte[] iv;
private boolean isLastPart;

private byte[] outputBuffer;

CipherSubscriber(Subscriber<? super ByteBuffer> wrappedSubscriber, Long contentLength, CryptographicMaterials materials, byte[] iv, boolean isLastPart) {
this.wrappedSubscriber = wrappedSubscriber;
this.contentLength = contentLength;
this.materials = materials;
this.iv = iv;
cipher = materials.getCipher(iv);
this.isLastPart = isLastPart;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,7 @@
import software.amazon.encryption.s3.materials.DecryptionMaterials;
import software.amazon.encryption.s3.materials.EncryptedDataKey;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
Expand Down Expand Up @@ -143,42 +138,23 @@ public void onStream(SdkPublisher<ByteBuffer> ciphertextPublisher) {
long[] desiredRange = RangedGetUtils.getRange(materials.getContentRange());
long[] cryptoRange = RangedGetUtils.getCryptoRange(materials.getContentRange());
AlgorithmSuite algorithmSuite = materials.algorithmSuite();
SecretKey contentKey = materials.dataKey();
final int tagLength = algorithmSuite.cipherTagLengthBits();
byte[] iv = contentMetadata.contentIv();
if (algorithmSuite == AlgorithmSuite.ALG_AES_256_CTR_IV16_TAG16_NO_KDF) {
iv = AesCtrUtils.adjustIV(iv, cryptoRange[0]);
}
try {
final Cipher cipher = CryptoFactory.createCipher(algorithmSuite.cipherName(), materials.cryptoProvider());
switch (algorithmSuite) {
case ALG_AES_256_GCM_IV12_TAG16_NO_KDF:
cipher.init(Cipher.DECRYPT_MODE, contentKey, new GCMParameterSpec(tagLength, iv));
break;
case ALG_AES_256_CTR_IV16_TAG16_NO_KDF:
case ALG_AES_256_CBC_IV16_NO_KDF:
cipher.init(Cipher.DECRYPT_MODE, contentKey, new IvParameterSpec(iv));
break;
default:
throw new S3EncryptionClientException("Unknown algorithm: " + algorithmSuite.cipherName());
}

if (algorithmSuite.equals(AlgorithmSuite.ALG_AES_256_CBC_IV16_NO_KDF)
|| algorithmSuite.equals(AlgorithmSuite.ALG_AES_256_CTR_IV16_TAG16_NO_KDF)
|| _enableDelayedAuthentication) {
// CBC and GCM with delayed auth enabled use a standard publisher
CipherPublisher plaintextPublisher = new CipherPublisher(ciphertextPublisher,
getObjectResponse.contentLength(), desiredRange, contentMetadata.contentRange(), algorithmSuite.cipherTagLengthBits(), materials, iv);
wrappedAsyncResponseTransformer.onStream(plaintextPublisher);
} else {
// Use buffered publisher for GCM when delayed auth is not enabled
BufferedCipherPublisher plaintextPublisher = new BufferedCipherPublisher(ciphertextPublisher,
getObjectResponse.contentLength(), materials, iv, _bufferSize);
wrappedAsyncResponseTransformer.onStream(plaintextPublisher);
}

} catch (GeneralSecurityException e) {
throw new S3EncryptionClientException("Unable to " + algorithmSuite.cipherName() + " content decrypt.", e);

if (algorithmSuite.equals(AlgorithmSuite.ALG_AES_256_CBC_IV16_NO_KDF)
|| algorithmSuite.equals(AlgorithmSuite.ALG_AES_256_CTR_IV16_TAG16_NO_KDF)
|| _enableDelayedAuthentication) {
// CBC and GCM with delayed auth enabled use a standard publisher
CipherPublisher plaintextPublisher = new CipherPublisher(ciphertextPublisher,
getObjectResponse.contentLength(), desiredRange, contentMetadata.contentRange(), algorithmSuite.cipherTagLengthBits(), materials, iv);
wrappedAsyncResponseTransformer.onStream(plaintextPublisher);
} else {
// Use buffered publisher for GCM when delayed auth is not enabled
BufferedCipherPublisher plaintextPublisher = new BufferedCipherPublisher(ciphertextPublisher,
getObjectResponse.contentLength(), materials, iv, _bufferSize);
wrappedAsyncResponseTransformer.onStream(plaintextPublisher);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,18 @@
import org.junit.jupiter.api.Test;
import software.amazon.encryption.s3.utils.S3EncryptionClientTestResources;

import static org.junit.jupiter.api.Assertions.fail;

public class AsyncClientExampleTest {

@Test
public void testAsyncClientExamples() {
final String bucket = S3EncryptionClientTestResources.BUCKET;
AsyncClientExample.main(new String[]{bucket});
try {
AsyncClientExample.main(new String[]{bucket});
} catch (Throwable exception) {
exception.printStackTrace();
fail("Async Example Test Failed!!", exception);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.fail;

public class ClientConfigurationExampleTest {
@Test
public void testClientConfigurationExamples() {
ClientConfigurationExample.main(new String[0]);
try {
ClientConfigurationExample.main(new String[0]);
} catch (Throwable exception) {
exception.printStackTrace();
fail("Client Configuration Example Test Failed!!", exception);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@
import org.junit.jupiter.api.Test;
import software.amazon.encryption.s3.utils.S3EncryptionClientTestResources;

import java.io.IOException;
import static org.junit.jupiter.api.Assertions.fail;

public class MultipartUploadExampleTest {

@Test
public void testMultipartUploadExamples() throws IOException {
public void testMultipartUploadExamples() {
final String bucket = S3EncryptionClientTestResources.BUCKET;
MultipartUploadExample.main(new String[]{bucket});
try {
MultipartUploadExample.main(new String[]{bucket});
} catch (Throwable exception) {
exception.printStackTrace();
fail("Multipart Example Test Failed!!", exception);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,18 @@
import org.junit.jupiter.api.Test;
import software.amazon.encryption.s3.utils.S3EncryptionClientTestResources;

import static org.junit.jupiter.api.Assertions.fail;

public class PartialKeyPairExampleTest {

@Test
public void testPartialKeyPairExamples() {
final String bucket = S3EncryptionClientTestResources.BUCKET;

PartialKeyPairExample.main(new String[]{bucket});
try {
PartialKeyPairExample.main(new String[]{bucket});
} catch (Throwable exception) {
exception.printStackTrace();
fail("Partial Key Pair Example Test Failed!!", exception);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,18 @@
import org.junit.jupiter.api.Test;
import software.amazon.encryption.s3.utils.S3EncryptionClientTestResources;

import static org.junit.jupiter.api.Assertions.fail;

public class RangedGetExampleTest {

@Test
public void testRangedGetExamples() {
final String bucket = S3EncryptionClientTestResources.BUCKET;
RangedGetExample.main(new String[]{bucket});
try {
RangedGetExample.main(new String[]{bucket});
} catch (Throwable exception) {
exception.printStackTrace();
fail("Ranged Get Test Failed!!", exception);
}
}
}