From 688b913218ec1de2a0ac286803e4d17ddd19ae37 Mon Sep 17 00:00:00 2001 From: arimu1 <19286898+arimu1@users.noreply.github.com> Date: Sun, 13 Sep 2026 06:10:15 +0700 Subject: [PATCH] fix(streamingaead): return -1 at EOF on empty seekable plaintext When plaintext size is zero, read() never decrypted the last segment so reachedEnd() stayed false and callers spun on 0 forever. Decrypt the final segment when at end-of-plaintext with no bytes transferred. Fixes #79 --- .../subtle/StreamingAeadSeekableDecryptingChannel.java | 3 +++ .../streamingaead/AesCtrHmacStreamingKeyManagerTest.java | 7 +------ .../streamingaead/AesGcmHkdfStreamingKeyManagerTest.java | 7 +------ 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/google/crypto/tink/subtle/StreamingAeadSeekableDecryptingChannel.java b/src/main/java/com/google/crypto/tink/subtle/StreamingAeadSeekableDecryptingChannel.java index 32371bebf..a38520833 100644 --- a/src/main/java/com/google/crypto/tink/subtle/StreamingAeadSeekableDecryptingChannel.java +++ b/src/main/java/com/google/crypto/tink/subtle/StreamingAeadSeekableDecryptingChannel.java @@ -342,6 +342,9 @@ public synchronized int read(ByteBuffer dst) throws IOException { } } int read = dst.position() - startPos; + if (read == 0 && !reachedEnd() && plaintextPosition >= plaintextSize) { + tryLoadSegment(numberOfSegments - 1); + } if (read == 0 && reachedEnd()) { return -1; } diff --git a/src/test/java/com/google/crypto/tink/streamingaead/AesCtrHmacStreamingKeyManagerTest.java b/src/test/java/com/google/crypto/tink/streamingaead/AesCtrHmacStreamingKeyManagerTest.java index b3766ec68..0e5bb44cc 100644 --- a/src/test/java/com/google/crypto/tink/streamingaead/AesCtrHmacStreamingKeyManagerTest.java +++ b/src/test/java/com/google/crypto/tink/streamingaead/AesCtrHmacStreamingKeyManagerTest.java @@ -312,11 +312,6 @@ public void decryptSeekableByteChannel_works( .isEqualTo(Arrays.copyOfRange(v.getPlaintext(), start, start + len)); // There must be no more data available. ByteBuffer endOfStreamChecker = ByteBuffer.allocate(1); - if (v.getPlaintext().length != 0) { - assertThat(plaintextChannel.read(endOfStreamChecker)).isEqualTo(-1); - } else { - // TODO: b/390077226 - This should return -1. - assertThat(plaintextChannel.read(endOfStreamChecker)).isEqualTo(0); - } + assertThat(plaintextChannel.read(endOfStreamChecker)).isEqualTo(-1); } } diff --git a/src/test/java/com/google/crypto/tink/streamingaead/AesGcmHkdfStreamingKeyManagerTest.java b/src/test/java/com/google/crypto/tink/streamingaead/AesGcmHkdfStreamingKeyManagerTest.java index 5da4b0723..f2f159a0d 100644 --- a/src/test/java/com/google/crypto/tink/streamingaead/AesGcmHkdfStreamingKeyManagerTest.java +++ b/src/test/java/com/google/crypto/tink/streamingaead/AesGcmHkdfStreamingKeyManagerTest.java @@ -406,11 +406,6 @@ public void decryptSeekableByteChannel_works( .isEqualTo(Arrays.copyOfRange(v.getPlaintext(), start, start + len)); // There must be no more data available. ByteBuffer endOfStreamChecker = ByteBuffer.allocate(1); - if (v.getPlaintext().length != 0) { - assertThat(plaintextChannel.read(endOfStreamChecker)).isEqualTo(-1); - } else { - // TODO: b/390077226 - This should return -1. - assertThat(plaintextChannel.read(endOfStreamChecker)).isEqualTo(0); - } + assertThat(plaintextChannel.read(endOfStreamChecker)).isEqualTo(-1); } }