From 66de8f71cb01587e610a3a40a0c8565b4b9f742e Mon Sep 17 00:00:00 2001 From: Shaun Arman Date: Sun, 29 Mar 2026 21:25:43 -0500 Subject: [PATCH] fix: set SQLCipher cipher_page_size BEFORE first database access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously cipher_page_size was set AFTER the verification SELECT, making it a no-op (SQLCipher locks page size on first access). This caused two bugs: 1. Databases were created with the default page size regardless of the setting, then flushed on close using misaligned 4KB mmap pages on 16KB kernel → corrupted file → SQLITE_NOTADB on reopen. 2. Reopening an existing DB used default (potentially wrong) page size for the initial read → decryption failure. Fix: batch all cipher settings (key, page_size, kdf_iter, algorithms) into a single execute_batch call BEFORE the first SELECT. This ensures: - New databases are created with 16KB pages (aligned to Asahi kernel) - Existing 16KB-page databases are reopened with the correct page size - Close/flush operations use properly aligned mmap → no corruption Note: existing 4KB-page databases (from v0.1.0) remain incompatible and must be deleted once on upgrade. Co-Authored-By: Claude Sonnet 4.6 (1M context) --- src-tauri/src/db/connection.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src-tauri/src/db/connection.rs b/src-tauri/src/db/connection.rs index 490b1911..7687f4f0 100644 --- a/src-tauri/src/db/connection.rs +++ b/src-tauri/src/db/connection.rs @@ -3,17 +3,20 @@ use std::path::Path; pub fn open_encrypted_db(path: &Path, key: &str) -> anyhow::Result { let conn = Connection::open(path)?; - // Set SQLCipher encryption key - conn.execute_batch(&format!("PRAGMA key = '{}';", key.replace('\'', "''")))?; - // Verify the key works by running a simple query - conn.execute_batch("SELECT count(*) FROM sqlite_master;")?; - // Set SQLCipher settings for AES-256 - conn.execute_batch( - "PRAGMA cipher_page_size = 16384; \ - PRAGMA kdf_iter = 256000; \ - PRAGMA cipher_hmac_algorithm = HMAC_SHA512; \ + // ALL cipher settings MUST be set before the first database access. + // cipher_page_size in particular must precede any read/write so it takes + // effect for both creation (new DB) and reopening (existing DB). + // 16384 matches 16KB kernel page size (Asahi Linux / Apple Silicon aarch64). + conn.execute_batch(&format!( + "PRAGMA key = '{}';\ + PRAGMA cipher_page_size = 16384;\ + PRAGMA kdf_iter = 256000;\ + PRAGMA cipher_hmac_algorithm = HMAC_SHA512;\ PRAGMA cipher_kdf_algorithm = PBKDF2_HMAC_SHA512;", - )?; + key.replace('\'', "''") + ))?; + // Verify the key and settings work + conn.execute_batch("SELECT count(*) FROM sqlite_master;")?; Ok(conn) }