StreamingAeadSeekableDecryptingChannel.read() never returns -1 — infinite CPU spin / hang on empty or truncated ciphertexts
評価
- 難易度
- 3/5
- 見積もり時間
- 1〜2日
- 初心者へのやさしさ
- 74/100
- issue の種類
- バグ
- 明瞭さ
- 明確に書かれている
- 活発さ
- 活発
- 技術スタック
- java
- 領域
- cryptography
調査の方向性
Start in StreamingAeadSeekableDecryptingChannel.java, focusing on read(), reachedEnd(), and the fill loop; review StreamingTestUtil to understand why EOF is not covered. Add regression coverage for empty, boundary, and truncated ciphertexts across AES-GCM-HKDF and AES-CTR-HMAC, and verify reads return -1 rather than repeatedly returning 0.
索引モデルが issue の本文から書いたものです。
説明
tink-java: StreamingAeadSeekableDecryptingChannel.read() never returns -1 — infinite CPU spin / hang on empty or truncated ciphertexts
Repo: tink-crypto/tink-java (OT0)
Summary
StreamingAeadSeekableDecryptingChannel (returned by the public
StreamingAead.newSeekableDecryptingChannel API) violates the
SeekableByteChannel contract: read(ByteBuffer) never returns -1 at
end-of-stream. Callers using the universal idiom spin forever burning CPU, and
Channels.newInputStream(ch).readAllBytes() / transferTo never return.
Verified against HEAD (810d752), PoC output:
valid empty-plaintext ciphertext: 56 bytes
HANG: valid-empty (seg 64): read() returned 0 one-million times (11 ms of CPU spin), never -1
HANG: valid-empty (seg 64): Channels.newInputStream(...).readAllBytes() did not return within 3 s
HANG: truncated-56B (seg 64): read() returned 0 one-million times, never -1
Affects both AES-GCM-HKDF and legacy AES-CTR-HMAC (shared plumbing).
Root cause
read() returns 0 whenever read == 0 && !reachedEnd()
(StreamingAeadSeekableDecryptingChannel.java). reachedEnd() (line 282) requires
isCurrentSegmentDecrypted && currentSegmentNr == numberOfSegments-1 && plaintextSegment.remaining() == 0. When plaintextSize == 0 — which happens for a
valid empty-plaintext ciphertext, and for attacker-truncated ciphertexts whose
computed size fills complete segments — the fill loop
while (dst.remaining() > 0 && plaintextPosition < plaintextSize) never runs, no
segment is ever decrypted, so reachedEnd() is permanently false and read() loops
on 0 forever.
Note the impact split:
- No attacker needed: encrypt an empty stream with the library itself, then read
it through the seekable channel — the service thread hangs. A valid
round-trip that hangs is a correctness bug on its own. - Attacker-controlled: decrypting an untrusted/truncated file via
newSeekableDecryptingChannelhangs the thread (truncation scan of a 70-byte
plaintext hangs at offsets 56/80/144 for segment size 64).
tink-go's reader handles the identical 56-byte ciphertext correctly (io.ReadAll
terminates — verified), so this is Java-specific. The existing test suite misses it
because StreamingTestUtil allocates plaintext.length-sized buffers and never
reads to EOF.
Fix direction
When the fill loop makes no progress and plaintextPosition >= plaintextSize (or
ciphertext is exhausted), decrypt the final (possibly empty) segment so
reachedEnd() becomes true and read() returns -1. Reproducer and a table-driven
regression test (empty, 0/1/±tag-boundary, truncated lengths) are ready to send.
Verified against HEAD (810d752). Reproducer harness output above was produced with the AES-GCM-HKDF keyset at segment size 64; AES-CTR-HMAC shares the plumbing and behaves identically.
- 主要言語
- Java
- スター
- 310
- フォーク
- 40
- PR マージ指標
- 30日以内にマージされた PR はありません
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートなし
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
tink-crypto/tink-java のほかの issue
-
java.lang.NoSuchMethodError: java.nio.ByteBuffer.position(I)Ljava/nio/ByteBuffer; in InsecureNonceChaCha20Poly1305Base on JDK8対応中かも @Sineaggi が 167 日前に担当しました。 オープン
難易度 3/5 1〜2日 初心者へのやさしさ 48/100
tink-crypto/tink-java#68 · コメント 13 件 ·
-
Support registering custom key managers for user-defined primitives (or provide a migration path)再び着手できるかも @tholenst が 248 日前に担当しましたが、オープン中のプルリクエストはありません。 オープン
tink-crypto/tink-java#67 · コメント 5 件 · 担当者 1 名 ·
-
p3
難易度 5/5 1週間以上 初心者へのやさしさ 25/100
tink-crypto/tink-java#66 · コメント 3 件 ·
-
p3
難易度 5/5 1週間以上 初心者へのやさしさ 25/100
tink-crypto/tink-java#41 · コメント 1 件 ·
-
p3
難易度 4/5 3〜5日 初心者へのやさしさ 25/100
tink-crypto/tink-java#26 · コメント 14 件 · リアクション 5 件 ·
tink-crypto/tink-java の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
snowflakedb/snowflake-kafka-connector#1610 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
swagger-api/swagger-petstore#245 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
liquid-java/liquidjava#443 ·
メンテナーはふだん 2 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 67/100
quarkiverse/quarkus-google-cloud-services#1188 ·
メンテナーはふだん 1 日以内に返信