in src/main/java/software/amazon/encryption/s3/internal/CipherSubscriber.java [139:183]
private void finalBytes() {
if (!finalBytesCalled.compareAndSet(false, true)) {
// already called, don't repeat
return;
}
// If this isn't the last part, skip doFinal and just send outputBuffer downstream.
// doFinal requires that all parts have been processed to compute the tag,
// so the tag will only be computed when the last part is processed.
if (!isLastPart) {
wrappedSubscriber.onNext(ByteBuffer.wrap(outputBuffer));
return;
}
// If this is the last part, compute doFinal and include its result in the value sent downstream.
// The result of doFinal MUST be included with the bytes that were in outputBuffer in the final onNext call.
byte[] finalBytes;
try {
finalBytes = cipher.doFinal();
} catch (final GeneralSecurityException exception) {
// Even if doFinal fails, downstream still expects to receive the bytes that were in outputBuffer
wrappedSubscriber.onNext(ByteBuffer.wrap(outputBuffer));
// Forward error, else the wrapped subscriber waits indefinitely
wrappedSubscriber.onError(exception);
throw new S3EncryptionClientSecurityException(exception.getMessage(), exception);
}
// Combine the bytes from outputBuffer and finalBytes into one onNext call.
// Downstream has requested one item in its request method, so this class can only call onNext once.
// This single onNext call must contain both the bytes from outputBuffer and the tag.
byte[] combinedBytes;
if (outputBuffer != null && outputBuffer.length > 0 && finalBytes != null && finalBytes.length > 0) {
combinedBytes = new byte[outputBuffer.length + finalBytes.length];
System.arraycopy(outputBuffer, 0, combinedBytes, 0, outputBuffer.length);
System.arraycopy(finalBytes, 0, combinedBytes, outputBuffer.length, finalBytes.length);
} else if (outputBuffer != null && outputBuffer.length > 0) {
combinedBytes = outputBuffer;
} else if (finalBytes != null && finalBytes.length > 0) {
combinedBytes = finalBytes;
} else {
combinedBytes = new byte[0];
}
wrappedSubscriber.onNext(ByteBuffer.wrap(combinedBytes));
}