private void finalBytes()

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));
    }