Version and environment
Reproduced on SQLCipher commit 810db22f575ee7cf94ea96a3e91622b5fcece3dc, built on Linux amd64. Runtime: SQLite 3.53.3 / SQLCipher 4.17.0 Community.
Describe the bug
Calling sqlite3_rekey() on a read-only encrypted database returns SQLITE_OK, although the write transaction/page rewrite fails and the database remains encrypted with its old key.
Minimal reproduction
bash sqlcipher-009-sqlite3-rekey-v2-returns-sqlite-ok-after-read-only-rek-6d6e162c/poc/run_revalidation.sh
The included C harness creates a database with old-pass, closes it, changes its mode to 0444, and opens it with:
sqlite3_open_v2("file:/tmp/sqlcipher_rekey_readonly_fresh.db?mode=ro", &db,
SQLITE_OPEN_READONLY | SQLITE_OPEN_URI, 0);
sqlite3_key(db, "old-pass", 8);
int rc = sqlite3_rekey(db, "new-pass", 8);
printf("readonly_rekey_rc=%d (%s)\n", rc, sqlite3_errstr(rc));
It then closes the handle and independently reopens the file with each key.
Actual behavior/output
readonly_rekey_rc=0 (not an error)
reopen_with_old_key_rc=0
reopen_with_new_key_rc=26 (file is not a database)
The new-key verification also emits the expected HMAC/NOTADB diagnostics, proving the persisted key did not change.
Expected behavior
A rekey operation requiring writes must return a non-OK status, such as SQLITE_READONLY, when invoked on a read-only connection. SQLITE_OK must mean that the database was actually rekeyed.
Impact and scope
Applications using the C API may record a successful key rotation, discard the old key, or update audit state even though no rotation occurred. This report is limited to the API-level return value on the main database.
Relevant source/root cause
In src/sqlcipher.c, sqlite3_rekey_v2() stores failures from sqlite3BtreeBeginTrans() and page writes in local rc around lines 3784-3809 and rolls back, but returns SQLITE_OK unconditionally around line 3816.
Suggested regression test
Open an encrypted database with mode=ro, call both sqlite3_rekey() and sqlite3_rekey_v2(), and assert a non-OK result. Reopen with both keys and assert the old key remains valid and the new key remains invalid.
Reproduction bundle
Attached: sqlcipher-009-sqlite3-rekey-v2-returns-sqlite-ok-after-read-only-rek-6d6e162c_attachment.zip. It contains the focused PoC, validation evidence, and candidate metadata.
sqlcipher-009-sqlite3-rekey-v2-returns-sqlite-ok-after-read-only-rek-6d6e162c_attachment.zip
Version and environment
Reproduced on SQLCipher commit
810db22f575ee7cf94ea96a3e91622b5fcece3dc, built on Linux amd64. Runtime: SQLite 3.53.3 / SQLCipher 4.17.0 Community.Describe the bug
Calling
sqlite3_rekey()on a read-only encrypted database returnsSQLITE_OK, although the write transaction/page rewrite fails and the database remains encrypted with its old key.Minimal reproduction
The included C harness creates a database with
old-pass, closes it, changes its mode to0444, and opens it with:It then closes the handle and independently reopens the file with each key.
Actual behavior/output
The new-key verification also emits the expected HMAC/NOTADB diagnostics, proving the persisted key did not change.
Expected behavior
A rekey operation requiring writes must return a non-OK status, such as
SQLITE_READONLY, when invoked on a read-only connection.SQLITE_OKmust mean that the database was actually rekeyed.Impact and scope
Applications using the C API may record a successful key rotation, discard the old key, or update audit state even though no rotation occurred. This report is limited to the API-level return value on the main database.
Relevant source/root cause
In
src/sqlcipher.c,sqlite3_rekey_v2()stores failures fromsqlite3BtreeBeginTrans()and page writes in localrcaround lines 3784-3809 and rolls back, but returnsSQLITE_OKunconditionally around line 3816.Suggested regression test
Open an encrypted database with
mode=ro, call bothsqlite3_rekey()andsqlite3_rekey_v2(), and assert a non-OK result. Reopen with both keys and assert the old key remains valid and the new key remains invalid.Reproduction bundle
Attached:
sqlcipher-009-sqlite3-rekey-v2-returns-sqlite-ok-after-read-only-rek-6d6e162c_attachment.zip. It contains the focused PoC, validation evidence, and candidate metadata.sqlcipher-009-sqlite3-rekey-v2-returns-sqlite-ok-after-read-only-rek-6d6e162c_attachment.zip