Skip to content

Changing cipher_plaintext_header_size after key use corrupts an encrypted database #614

Description

@DBFuzz

Version and environment

Reproduced on SQLCipher commit 810db22f575ee7cf94ea96a3e91622b5fcece3dc, Linux amd64, with a fresh runtime reporting SQLite 3.53.3 / SQLCipher 4.17.0 Community.

Describe the bug

Unlike neighboring format setters, sqlcipher_codec_ctx_set_plaintext_header_size() does not reject changes after the key has been derived and used. Changing cipher_plaintext_header_size from 0 to 16 after initial reads/writes changes page-1 crypto offsets mid-connection. Further writes and integrity_check report success, but the committed database cannot be reopened with the correct key.

Minimal reproduction

bash sqlcipher-013-post-key-cipher-plaintext-header-size-corrupts-an-encr-cff04674/poc/run_revalidation.sh

The trigger sequence is:

PRAGMA key='testkey';
CREATE TABLE t1(v);
INSERT INTO t1 VALUES('before');
SELECT count(*) FROM t1;
PRAGMA cipher_plaintext_header_size=16;
INSERT INTO t1 VALUES('after');
SELECT count(*) FROM t1;
PRAGMA integrity_check;

Close the database, reopen the same file with PRAGMA key='testkey', then run SELECT count(*) FROM t1; PRAGMA integrity_check;. The supplied control sets a fixed salt and plaintext-header size before any page use and successfully reopens.

Actual behavior/output

before_count|1
16
after_count|2
ok
first_connection_exit=0
reopen_with_correct_key_exit=1

Reopen stderr includes sqlcipher_page_cipher: hmac check failed for pgno=1 and file is not a database (26). The pre-key control creates and reopens successfully.

Expected behavior

After a key has been used, a setting that changes page-1 header/salt/encryption layout should be rejected or ignored before any state changes. A successful write must not leave the file unreadable with the correct key.

Impact and scope

An accepted PRAGMA can persistently corrupt an otherwise valid encrypted database. The first connection provides misleading success, so the damage may be discovered only after close/reopen.

Relevant source/root cause

Sibling setters around src/sqlcipher.c:1396, :1418, and :1450 check CIPHER_FLAG_KEY_USED. The plaintext-header setter at :1439-1442 lacks that guard. The PRAGMA branch at :3006-3012 also ignores the setter result. Page-1 read/write code around :3442-3450 and :3484-3493 uses the mutable header size as its offset.

Suggested regression test

Create and read/write an encrypted database, then set cipher_plaintext_header_size=16. Assert that the setting is rejected or unchanged, and verify after close/reopen that all rows remain readable and both integrity checks pass.

Reproduction bundle

Attached: sqlcipher-013-post-key-cipher-plaintext-header-size-corrupts-an-encr-cff04674_attachment.zip. It contains the focused PoC, validation evidence, and candidate metadata.

sqlcipher-013-post-key-cipher-plaintext-header-size-corrupts-an-encr-cff04674_attachment.zip

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions