Skip to content

Fix the ChaCha20 overflow check for start counter 0xffffffff - #299

Merged
utelle merged 2 commits into
utelle:mainfrom
SchwarzDigits:fix/chacha20-wrap-start-counter
Nov 3, 2026
Merged

utelle merged 2 commits into
utelle:mainfrom
SchwarzDigits:fix/chacha20-wrap-start-counter

Conversation

@mtrossbach

Copy link
Copy Markdown
Contributor

Follow-up to 77827fa. chacha20_counter_wraps() expects the page counter (the call starts at counter + 1), but chacha20_xor() passes the start counter of the call. For the start counter 0xffffffff, i.e. page counter 0xfffffffe, the check sees no overflow, and the accelerated code carries the counter into the first nonce word. Pages with this counter that version 2.5 or earlier wrote are then still read incorrectly with hardware acceleration.

The function now takes the start counter of the call, and the page and VLE encryption pass counter + 1. The counters chosen when encrypting stay the same (checked for all 2^32 values), and the check in chacha20_xor now flags exactly the start counters at which the counter overflows.

The second commit adds the case to cryptotest: for lengths of 1024 bytes and more, chacha20_xor has to match sqleet for every start counter from just below the overflow up to 0xffffffff. On the current main it fails for 0xffffffff.

Testing (macOS arm64 with NEON, x86_64 with SSSE3 under Rosetta):

  • cryptotest with the new case fails on 3186f7e and passes with the fix.
  • A page written by 2.5.1 with page counter 0xfffffffe: on 3186f7e an overflow page reads back a wrong value without an error, and a table leaf page gives "malformed". With the fix both read correctly.
  • The SQL scripts from the CI, the four tempfiletest runs, oomtest and rekeytest pass.

chacha20_counter_wraps() assumed that the call starts at counter + 1.
That holds for the page and VLE encryption, but chacha20_xor() passes
the start counter of the call itself. For the start counter 0xffffffff
(page counter 0xfffffffe) the check saw no overflow, so the accelerated
code was used and carried the counter into the nonce. A page with this
counter written by version 2.5 or earlier was still read incorrectly
on x86 and ARM64.

The function now takes the start counter of the call, and the two
encrypting callers pass counter + 1. The counters chosen when
encrypting do not change.
For lengths that use the accelerated code, chacha20_xor has to give the
same result as sqleet for every start counter from just below the
overflow up to 0xffffffff. Without the previous commit the test fails
for the start counter 0xffffffff.
@utelle
utelle merged commit 8025d0c into utelle:main Nov 3, 2026
13 checks passed
@utelle

utelle commented Nov 3, 2026

Copy link
Copy Markdown
Owner

Thanks for detecting and fixing this glitch.

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.

2 participants