Skip to content

Fix Grain-128AEAD streamed decryption writing its second output segment over the first - #2447

Open
Arpan0995 wants to merge 1 commit into
bcgit:mainfrom
Arpan0995:aead-stream-decrypt-second-segment-offset
Open

Arpan0995 wants to merge 1 commit into
bcgit:mainfrom
Arpan0995:aead-stream-decrypt-second-segment-offset

Conversation

@Arpan0995

Copy link
Copy Markdown

AEADBaseEngine.StreamCipherOperator.processBytes splits a decryption call that spans the buffered authentication tag into two output segments: bytes released from the tag buffer, then bytes taken straight from the input. The first is written at outOff and advances the running count rlt, but the second is written at outOff as well instead of at outOff + rlt. The second therefore overwrites the head of the first, and the tail of the reported output is never written. The call still returns the full byte count, and the state update does not depend on where the output lands, so doFinal still accepts the tag: the wrong plaintext comes back with nothing raised. The equivalent step of the general decryption path, processEncDecBytes, already writes at outOff + resultLength.

Grain128AEADEngine is the only engine that selects DataOperatorType.StreamCipher, so this is confined to chunked Grain-128AEAD decryption, where a chunk after the first carries more than the 8-byte MAC_SIZE. One-shot decryption, the encryption path and all other AEAD engines are unaffected. Grain-128AEAD is lightweight-API only, so callers construct Grain128AEADEngine directly.

Reproduced on the released bcprov-jdk18on-1.86.jar: across a sweep of chunk boundaries 9156 of 12800 streamed decryptions returned plaintext differing from the one-shot result; a separate split sweep recorded 63 wrong plaintexts with zero tag failures and zero exceptions, and a 16 KiB read loop returned 12232 wrong bytes at a 4096-byte read size with the tag verifying. This is not fallout from the 1.86 lightweight-AEAD performance work, which covered Elephant, PhotonBeetle, Romulus and GIFT-COFB, not Grain-128AEAD: the 1.85.2 source reproduces the identical failure, the one functional change to AEADBaseEngine between them is the processByte single-byte buffer, and the same call is present as far back as 1.81. Current main and the 1.87-SNAPSHOT beta (build 1.87.0.20709) match 1.86 here in source and in compiled classes, and both reproduce it.

This change:

  • writes the second segment at outOff + rlt; and
  • adds a testStreamedDecryption to Grain128AEADTest that splits a ciphertext at every point and asserts the streamed plaintext matches the one-shot decryption and the original, plus a CipherTest.checkAEADCipherMultipleBlocks call at PARTLEN 19 beside the existing 7. The existing call never reaches the second segment, since len > MAC_SIZE is never true at PARTLEN 7; testSplitUpdate only initialises for encryption. Both added checks fail without the change and pass with it; the nine pre-existing checks pass either way, LWC_AEAD_KAT_128_96.txt included.

Base tree only; no module-info or OSGi change is needed. A release-note entry is included, happy to move it to another block.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant