Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

StreamingAeadSeekableDecryptingChannel.read() never returns -1 — infinite CPU spin / hang on empty or truncated ciphertexts

Aperta
#79 0 commenti 0 reazioni 1 assegnatario Vedi su GitHub

@willinois ci sta già lavorando.

Dal 21/9/2026.

  • #81 di @arimu1 — aperta

Valutazione

Difficoltà
3/5
Tempo stimato
1-2 giorni
Idoneità per principianti
74/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
java
Ambito
cryptography

Direzione di ricerca

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.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

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
    newSeekableDecryptingChannel hangs 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.

Lingua principale
Java
Stelle
308
Fork
41
Metriche di merge delle PR
Nessuna PR unita negli ultimi 30g

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di tink-crypto/tink-java

Tutte le issue di tink-crypto/tink-java

Issue simili

Altre issue su Java

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.