Skip to content

Compare SQLCipher HMAC values in constant time - #264

Merged
utelle merged 1 commit into
utelle:mainfrom
SchwarzDigits:fix/sqlcipher-hmac-ct-compare
Sep 11, 2026
Merged

utelle merged 1 commit into
utelle:mainfrom
SchwarzDigits:fix/sqlcipher-hmac-ct-compare

Conversation

@mtrossbach

Copy link
Copy Markdown
Contributor

Hi Ulrich,

here's another small finding, as mentioned in #262. The fix is tiny, so I'm sending it directly as a PR.

The authenticated cipher schemes in sqlite3mc compare their authentication tags in constant time:

  • ChaCha20 with poly1305_tagcmp(),
  • AEGIS with aegis_verify_16/32(),
  • Ascon in ascon_aead_decrypt().

The sqlcipher scheme is the only exception: DecryptPageSQLCipherCipher() in src/cipher_sqlcipher.c checks the page HMAC with memcmp(). SQLCipher itself also uses a constant-time comparison (sqlcipher_memcmp()).

Why it matters: memcmp() may stop at the first differing byte. The time the check takes can therefore reveal how many leading bytes of a forged HMAC are correct (CWE-208). In practice this is hard to exploit, because an attacker needs write access to the file and very precise timing of many page reads. But it's a typical finding in security reviews, and it's easy to fix.

The change (only src/cipher_sqlcipher.c, +18/−1):

  • A static helper CompareHmacSQLCipherCipher() combines all byte differences and evaluates the result only at the end, following the same pattern as poly1305_tagcmp().
  • It keeps memcmp()'s convention (0 means equal), so the call site only changes the function name.
  • Behaviour and file format stay unchanged.
  • The patch also applies cleanly to the hwaccel branch.
How I tested it
  • macOS (Apple M4 Max, Apple clang) and Linux aarch64 (GCC 13): the CI test scripts produce identical output before and after the change, and there are no new compiler warnings.
  • Tamper tests with sqlcipher (legacy=4 and the default setting): flipping a single byte in the ciphertext, IV or HMAC of a page is still rejected. Untouched databases open as before.

Best regards,
Markus

The HMAC verification of the SQLCipher cipher scheme used memcmp(), whose
execution time may depend on the position of the first differing byte.
Use a constant-time comparison instead, as SQLCipher itself does and as
the ChaCha20-Poly1305 cipher scheme already does for its Poly1305 tags.
@utelle
utelle merged commit cc76e25 into utelle:main Sep 11, 2026
9 checks passed
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