Skip to content

Commit caeac1d

Browse files
author
Lucas McDonald
committed
m
1 parent 4527ce1 commit caeac1d

File tree

1 file changed

+31
-29
lines changed

1 file changed

+31
-29
lines changed

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

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -47,23 +47,39 @@ public void onNext(ByteBuffer byteBuffer) {
4747
byte[] buf = BinaryUtils.copyBytesFrom(byteBuffer, amountToReadFromByteBuffer);
4848
outputBuffer = cipher.update(buf, 0, amountToReadFromByteBuffer);
4949
if (outputBuffer == null || outputBuffer.length == 0) {
50-
// The underlying data is too short to fill in the block cipher.
51-
// Note that while the JCE Javadoc specifies that the outputBuffer is null in this case,
52-
// in practice SunJCE and ACCP return an empty buffer instead, hence checks for
53-
// null OR length == 0.
54-
if (contentRead.get() == contentLength) {
55-
// All content has been read, so complete to get the final bytes
56-
this.onComplete();
57-
}
58-
// Otherwise, wait for more bytes. To avoid blocking,
59-
// send an empty buffer to the wrapped subscriber.
60-
wrappedSubscriber.onNext(ByteBuffer.allocate(0));
50+
// No bytes provided from upstream; just return.
51+
// No need to emit empty bytes to downstream; this can be misinterpreted.
52+
return;
6153
} else {
62-
wrappedSubscriber.onNext(ByteBuffer.wrap(outputBuffer));
54+
boolean atEnd = isLastPart && contentRead.get() + amountToReadFromByteBuffer >= contentLength;
55+
56+
if (atEnd) {
57+
// If all content has been read, send the final bytes in this onNext call.
58+
// The final bytes must be sent with the final onNext call, not during the onComplete call.
59+
byte[] finalBytes;
60+
try {
61+
finalBytes = cipher.doFinal();
62+
} catch (final GeneralSecurityException exception) {
63+
wrappedSubscriber.onError(exception);
64+
throw new S3EncryptionClientSecurityException(exception.getMessage(), exception);
65+
}
66+
67+
// Combine outputBuffer and finalBytes if both exist
68+
byte[] combinedBuffer;
69+
if (outputBuffer != null && outputBuffer.length > 0) {
70+
combinedBuffer = new byte[outputBuffer.length + finalBytes.length];
71+
System.arraycopy(outputBuffer, 0, combinedBuffer, 0, outputBuffer.length);
72+
System.arraycopy(finalBytes, 0, combinedBuffer, outputBuffer.length, finalBytes.length);
73+
} else {
74+
combinedBuffer = finalBytes;
75+
}
76+
wrappedSubscriber.onNext(ByteBuffer.wrap(combinedBuffer));
77+
return;
78+
} else {
79+
// Not at end; send content so far
80+
wrappedSubscriber.onNext(ByteBuffer.wrap(outputBuffer));
81+
}
6382
}
64-
} else {
65-
// Do nothing
66-
wrappedSubscriber.onNext(byteBuffer);
6783
}
6884
}
6985

@@ -91,20 +107,6 @@ public void onError(Throwable t) {
91107

92108
@Override
93109
public void onComplete() {
94-
if (!isLastPart) {
95-
// If this isn't the last part, skip doFinal, we aren't done
96-
wrappedSubscriber.onComplete();
97-
return;
98-
}
99-
try {
100-
outputBuffer = cipher.doFinal();
101-
// Send the final bytes to the wrapped subscriber
102-
wrappedSubscriber.onNext(ByteBuffer.wrap(outputBuffer));
103-
} catch (final GeneralSecurityException exception) {
104-
// Forward error, else the wrapped subscriber waits indefinitely
105-
wrappedSubscriber.onError(exception);
106-
throw new S3EncryptionClientSecurityException(exception.getMessage(), exception);
107-
}
108110
wrappedSubscriber.onComplete();
109111
}
110112

0 commit comments

Comments
 (0)