diff --git a/src/crypto.c b/src/crypto.c index f396ac7b..3423b0a5 100644 --- a/src/crypto.c +++ b/src/crypto.c @@ -238,6 +238,11 @@ int sqlite3CodecAttach(sqlite3* db, int nDb, const void *zKey, int nKey) { codec_set_btree_to_codec_pagesize(db, pDb, ctx); + /* force secure delete. This has the benefit of wiping internal data when deleted + and also ensures that all pages are written to disk (i.e. not skipped by + sqlite3PagerDontWrite optimizations) */ + sqlite3BtreeSecureDelete(pDb->pBt, 1); + /* if fd is null, then this is an in-memory database and we dont' want to overwrite the AutoVacuum settings if not null, then set to the default */ @@ -312,11 +317,14 @@ int sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey) { rc = sqlite3PagerGet(pPager, pgno, &page); if(rc == SQLITE_OK) { /* write page see pager_incr_changecounter for example */ rc = sqlite3PagerWrite(page); - //printf("sqlite3PagerWrite(%d)\n", pgno); if(rc == SQLITE_OK) { sqlite3PagerUnref(page); - } - } + } else { + CODEC_TRACE(("sqlite3_rekey: error %d occurred writing page %d\n", rc, pgno)); + } + } else { + CODEC_TRACE(("sqlite3_rekey: error %d occurred getting page %d\n", rc, pgno)); + } } } diff --git a/src/crypto_impl.c b/src/crypto_impl.c index 07b21d98..9e86be81 100644 --- a/src/crypto_impl.c +++ b/src/crypto_impl.c @@ -208,6 +208,8 @@ int sqlcipher_cipher_ctx_cmp(cipher_ctx *c1, cipher_ctx *c2) { && c1->fast_kdf_iter == c2->fast_kdf_iter && c1->key_sz == c2->key_sz && c1->pass_sz == c2->pass_sz + && c1->use_hmac == c2->use_hmac + && c1->hmac_sz == c2->hmac_sz && ( c1->pass == c2->pass || !sqlcipher_memcmp((const unsigned char*)c1->pass, diff --git a/test/crypto.test b/test/crypto.test index cefe533a..eb43a167 100644 --- a/test/crypto.test +++ b/test/crypto.test @@ -201,6 +201,186 @@ do_test rekey-as-first-operation { db close file delete -force test.db +# create a new database, insert some data +# then rekey it with the same password +do_test rekey-same-passkey { + sqlite_orig db test.db + + execsql { + PRAGMA key = 'test123'; + CREATE TABLE t1(a,b); + BEGIN; + } + + for {set i 1} {$i<=1000} {incr i} { + set r [expr {int(rand()*500000)}] + execsql "INSERT INTO t1 VALUES($i,'value $r');" + } + + execsql { + COMMIT; + SELECT count(*) FROM t1; + PRAGMA rekey = 'test123'; + SELECT count(*) FROM t1; + } +} {1000 1000} +db close +file delete -force test.db + +# create a new database, insert some data +# then rekey it. Make sure it is immediately +# readable. Then close it and make sure it can be +# read back +do_test rekey-and-query-1 { + sqlite_orig db test.db + + execsql { + PRAGMA key = 'test123'; + CREATE TABLE t1(a,b); + BEGIN; + } + + for {set i 1} {$i<=1000} {incr i} { + set r [expr {int(rand()*500000)}] + execsql "INSERT INTO t1 VALUES($i,'value $r');" + } + + execsql { + COMMIT; + SELECT count(*) FROM t1; + PRAGMA rekey = 'test321'; + SELECT count(*) FROM t1; + } +} {1000 1000} + +db close + +do_test rekey-and-query-2 { + sqlite_orig db test.db + execsql { + PRAGMA key = 'test321'; + SELECT count(*) FROM t1; + } +} {1000} +db close +file delete -force test.db + +# create a new database, insert some data +# delete about 50% of the data +# write some new data +# delete another 50% +# then rekey it. Make sure it is immediately +# readable. Then close it and make sure it can be +# read back. This test will ensure that Secure Delete +# is enabled and all pages are being written and are not +# being optimized out by sqlite3PagerDontWrite +do_test rekey-delete-and-query-1 { + sqlite_orig db test.db + + execsql { + PRAGMA key = 'test123'; + CREATE TABLE t1(a,b); + CREATE INDEX ta_a ON t1(a); + BEGIN; + } + + for {set i 1} {$i<1000} {incr i} { + set r [expr {int(rand()*32767)}] + set r1 [expr {int(rand()*32767)}] + execsql "INSERT INTO t1 VALUES($r,$r1);" + } + set r [expr {int(rand()*32767)}] + set r1 [expr {int(rand()*32767)}] + execsql "UPDATE t1 SET b = $r WHERE a < $r1;" + + set r [expr {int(rand()*32767)}] + + execsql "DELETE FROM t1 WHERE a < $r;" + + execsql { + COMMIT; + SELECT (count(*) > 0) FROM t1; + } +} {1} +db close + +do_test rekey-delete-and-query-2 { + sqlite_orig db test.db + execsql { + PRAGMA key = 'test123'; + PRAGMA rekey = 'test321'; + SELECT count(*) > 1 FROM t1; + PRAGMA integrity_check; + } +} {1 ok} +db close + +do_test rekey-delete-and-query-3 { + sqlite_orig db test.db + execsql { + PRAGMA key = 'test321'; + SELECT count(*) > 1 FROM t1; + } +} {1} +db close +file delete -force test.db + + +# same as previous test, but use WAL +do_test rekey-delete-and-query-wal-1 { + sqlite_orig db test.db + + execsql { + PRAGMA key = 'test123'; + PRAGMA journal_mode = WAL; + CREATE TABLE t1(a,b); + CREATE INDEX ta_a ON t1(a); + BEGIN; + } + + for {set i 1} {$i<1000} {incr i} { + set r [expr {int(rand()*32767)}] + set r1 [expr {int(rand()*32767)}] + execsql "INSERT INTO t1 VALUES($r,$r1);" + } + set r [expr {int(rand()*32767)}] + set r1 [expr {int(rand()*32767)}] + execsql "UPDATE t1 SET b = $r WHERE a < $r1;" + + set r [expr {int(rand()*32767)}] + + execsql "DELETE FROM t1 WHERE a < $r;" + + execsql { + COMMIT; + SELECT (count(*) > 0) FROM t1; + } +} {1} +db close + +do_test rekey-delete-and-query-wal-2 { + sqlite_orig db test.db + execsql { + PRAGMA key = 'test123'; + PRAGMA journal_mode = WAL; + PRAGMA rekey = 'test321'; + SELECT count(*) > 1 FROM t1; + PRAGMA integrity_check; + } +} {wal 1 ok} +db close + +do_test rekey-delete-and-query-wal-3 { + sqlite_orig db test.db + execsql { + PRAGMA key = 'test321'; + PRAGMA journal_mode = WAL; + SELECT count(*) > 1 FROM t1; + } +} {wal 1} +db close +file delete -force test.db + # attach an encrypted database # where both database have the same # key @@ -1217,4 +1397,70 @@ do_test verify-pragma-cipher-version { db close file delete -force test.db +# create a new database, insert some data +# and delete some data with +# auto_vacuum on +do_test auto-vacuum-full { + sqlite_orig db test.db + + execsql { + PRAGMA key = 'test123'; + PRAGMA auto_vacuum = FULL; + CREATE TABLE t1(a,b); + BEGIN; + } + + for {set i 1} {$i<10000} {incr i} { + set r [expr {int(rand()*32767)}] + set r1 [expr {int(rand()*32767)}] + execsql "INSERT INTO t1 VALUES($r,$r1);" + } + set r [expr {int(rand()*32767)}] + execsql "DELETE FROM t1 WHERE a < $r;" + + execsql { + COMMIT; + PRAGMA integrity_check; + PRAGMA freelist_count; + SELECT (count(*) > 0) FROM t1; + } +} {ok 0 1} +db close +file delete -force test.db + +# create a new database, insert some data +# and delete some data with +# auto_vacuum incremental +do_test auto-vacuum-incremental { + sqlite_orig db test.db + + execsql { + PRAGMA key = 'test123'; + PRAGMA auto_vacuum = INCREMENTAL; + CREATE TABLE t1(a,b); + BEGIN; + } + + for {set i 1} {$i<10000} {incr i} { + set r [expr {int(rand()*32767)}] + set r1 [expr {int(rand()*32767)}] + execsql "INSERT INTO t1 VALUES($r,$r1);" + } + set r [expr {int(rand()*32767)}] + execsql "DELETE FROM t1 WHERE a < $r;" + + execsql { + COMMIT; + PRAGMA incremental_vacuum; + PRAGMA freelist_count; + PRAGMA integrity_check; + SELECT (count(*) > 0) FROM t1; + } +} {0 ok 1} +db close +file delete -force test.db + + + + finish_test diff --git a/tool/crypto-speedtest.tcl b/tool/crypto-speedtest.tcl index ca6e0b4e..fa923a01 100755 --- a/tool/crypto-speedtest.tcl +++ b/tool/crypto-speedtest.tcl @@ -95,8 +95,6 @@ catch {exec /bin/sh -c {rm -f perftest*.db}} set fd [open perftest0.sql w] puts $fd { -PRAGMA key='xyzzy'; -PRAGMA cipher_use_hmac=OFF; } close $fd