# SQLCipher # codec.test developed by Stephen Lombardo (Zetetic LLC) # sjlombardo at zetetic dot net # http://zetetic.net # # Copyright (c) 2018, ZETETIC LLC # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # * Neither the name of the ZETETIC LLC nor the # names of its contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # This file implements regression tests for SQLite library. The # focus of this script is testing code cipher features. # # NOTE: tester.tcl has overridden the definition of sqlite3 to # automatically pass in a key value. Thus tests in this file # should explicitly close and open db with sqlite_orig in order # to bypass default key assignment. set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/sqlcipher.tcl set hexkeyspec "\"x'98483C6EB40B6C31A448C22A66DED3B5E5E8D5119CAC8327B655C8B5C483648101010101010101010101010101010101'\"" # verify default plaintext header size is 0 do_test test-default-plaintext-header-size { sqlite_orig db :memory: execsql { PRAGMA cipher_default_plaintext_header_size; } } {0} # verify pragma cipher_salt returns the first 16 bytes # of an existing database do_test test-pragma-salt-get { sqlite_orig db test.db execsql { PRAGMA key = 'test'; } set salt [execsql { CREATE TABLE t1(a,b); PRAGMA cipher_salt; }] set header [string tolower [hexio_read test.db 0 16]] string equal $header $salt } {1} file delete -force test.db # explicitly set the salt of a new database do_test test-pragma-salt-set { set rc {} sqlite_orig db test.db execsql { PRAGMA key = 'test'; PRAGMA cipher_salt = "x'01010101010101010101010101010101'"; CREATE TABLE t1(a,b); INSERT INTO t1(a,b) VALUES (1,2); } db close lappend rc [hexio_read test.db 0 16] sqlite_orig db test.db lappend rc [execsql " PRAGMA key = 'test'; SELECT count(*) FROM t1; PRAGMA cipher_salt; "] } {01010101010101010101010101010101 {ok 1 01010101010101010101010101010101}} file delete -force test.db # verify that a raw key with a fixed salt will work # the first 16 bytes of database should be equal to the specified salt # which is the last 32 characters of the hex key spec. # also verify return value of cipher_salt do_test test-raw-key-with-salt-spec { set rc {} sqlite_orig db test.db execsql " PRAGMA key = $hexkeyspec; CREATE TABLE t1(a,b); INSERT INTO t1(a,b) VALUES (1,2); " db close lappend rc [hexio_read test.db 0 16] sqlite_orig db test.db lappend rc [execsql " PRAGMA key = $hexkeyspec; SELECT count(*) FROM t1; PRAGMA cipher_salt; "] } {01010101010101010101010101010101 {ok 1 01010101010101010101010101010101}} db close file delete -force test.db # verify that a raw key with an invalid salt will not work to # open an existing database. # should cause hmac failure due to invalid generated HMAC key do_test test-raw-key-with-invalid-salt-spec { sqlite_orig db test.db execsql " PRAGMA key = $hexkeyspec; CREATE TABLE t1(a,b); INSERT INTO t1(a,b) VALUES (1,2); " db close sqlite_orig db test.db catchsql { PRAGMA key="x'98483C6EB40B6C31A448C22A66DED3B5E5E8D5119CAC8327B655C8B5C483648100000000000000000000000000000001'"; SELECT count(*) FROM t1; } } {1 {file is not a database}} db close file delete -force test.db # verify that a raw key with a bad salt *will* work if page HMAC is disabled # in this case the salt will not actually be used for anything # because the encryption key is provided explicitly do_test test-raw-key-with-invalid-salt-spec-no-hmac { sqlite_orig db test.db execsql " PRAGMA key = $hexkeyspec; PRAGMA cipher_use_hmac = OFF; CREATE TABLE t1(a,b); INSERT INTO t1(a,b) VALUES (1,2); " db close sqlite_orig db test.db execsql { PRAGMA key="x'98483C6EB40B6C31A448C22A66DED3B5E5E8D5119CAC8327B655C8B5C483648100000000000000000000000000000001'"; PRAGMA cipher_use_hmac = OFF; SELECT count(*) FROM t1; } } {ok 1} db close file delete -force test.db # verify that invalid cipher_plaintext_header_sizes don't work # 1. less than zero # 2. Larger than available page size # 2. Not a multiple of block size do_test test-invalid-plaintext-header-sizes { set rc {} sqlite_orig db test.db lappend rc [catchsql " PRAGMA key = $hexkeyspec; PRAGMA cipher_plaintext_header_size = -1; CREATE TABLE t1(a,b); "] db close sqlite_orig db test.db lappend rc [catchsql " PRAGMA key = $hexkeyspec; PRAGMA cipher_plaintext_header_size = 4096; CREATE TABLE t1(a,b); "] db close sqlite_orig db test.db lappend rc [catchsql " PRAGMA key = $hexkeyspec; PRAGMA cipher_plaintext_header_size = 24; CREATE TABLE t1(a,b); "] } {{1 {SQL logic error}} {1 {SQL logic error}} {1 {SQL logic error}}} db close file delete -force test.db # verify that a valid cipher_plaintext_header_size leaves the # start of the database unencrypted, i.e. "SQLite format 3\0" do_test test-valid-plaintext-header-size { set rc {} sqlite_orig db test.db execsql " PRAGMA key = $hexkeyspec; PRAGMA cipher_plaintext_header_size = 16; CREATE TABLE t1(a,b); INSERT INTO t1(a,b) VALUES (1,2); " db close lappend rc [hexio_read test.db 0 16] sqlite_orig db test.db lappend rc [execsql " PRAGMA key = $hexkeyspec; PRAGMA cipher_plaintext_header_size = 16; SELECT count(*) FROM t1; PRAGMA cipher_plaintext_header_size; "] } {53514C69746520666F726D6174203300 {ok 1 16}} db close file delete -force test.db # when using a standard mode database and 32 byte # plaintext header, ensure that bytes 16 - 19 # corresponding to the page size and file versions, and reserve size # are readable and equal to 1024, 1, 1, and 80 respectively do_test test-plaintext-header-journal-delete-mode-readable { sqlite_orig db test.db execsql { PRAGMA key = 'test'; PRAGMA cipher_plaintext_header_size = 32; CREATE TABLE t1(a,b); INSERT INTO t1(a,b) VALUES (1,2); } db close string equal [hexio_read test.db 16 5] "1000010150" } {1} file delete -force test.db # when using a WAL mode database and 32 byte # plaintext header, ensure that bytes 16 - 19 # corresponding to the page size and file versions, and reserve size # are readable and equal to 1024, 2, 2 and 80 respectively do_test test-plaintext-header-journal-wal-mode-readable { sqlite_orig db test.db execsql { PRAGMA key = 'test'; PRAGMA cipher_plaintext_header_size = 32; PRAGMA journal_mode = WAL; CREATE TABLE t1(a,b); INSERT INTO t1(a,b) VALUES (1,2); } db close string equal [hexio_read test.db 16 5] "1000020250" } {1} file delete -force test.db # verify that a valid default_cipher_plaintext_header_size leaves the # start of the database unencrypted right from the start # , i.e. "SQLite format 3\0" do_test test-valid-default-plaintext-header-size { set rc {} sqlite_orig db test.db execsql { PRAGMA cipher_default_plaintext_header_size = 16; PRAGMA key = 'test'; } set salt [execsql { CREATE TABLE t1(a,b); INSERT INTO t1(a,b) VALUES (1,2); PRAGMA cipher_salt; }] db close lappend rc [hexio_read test.db 0 16] sqlite_orig db test.db execsql { PRAGMA key = 'test'; } lappend rc [execsql " PRAGMA cipher_salt = \"x'$salt'\"; SELECT count(*) FROM t1; PRAGMA cipher_plaintext_header_size; "] # reset the default back to 0 or subsequent tests will fail execsql "PRAGMA cipher_default_plaintext_header_size = 0;" lappend rc [string equal $salt "53514c69746520666f726d6174203300"] } {53514C69746520666F726D6174203300 {1 16} 0} db close file delete -force test.db # verify that a valid default_cipher_plaintext_header_size # operates properly on an attached database, and that the # salt pragma operates on the attached database as well do_test test-valid-default-plaintext-header-size-attach { set rc {} sqlite_orig db test.db execsql { PRAGMA cipher_default_plaintext_header_size = 16; PRAGMA key = 'test'; } set salt [execsql { CREATE TABLE temp(a); ATTACH DATABASE 'test2.db' as db2; CREATE TABLE db2.t2(a,b); INSERT INTO db2.t2(a,b) VALUES (1,2); PRAGMA db2.cipher_salt; DETACH DATABASE db2; }] db close lappend rc [hexio_read test2.db 0 16] sqlite_orig db test2.db execsql { PRAGMA key = 'test'; } lappend rc [execsql " PRAGMA cipher_salt = \"x'$salt'\"; SELECT count(*) FROM t2; PRAGMA cipher_plaintext_header_size; "] # reset the default back to 0 or subsequent tests will fail execsql "PRAGMA cipher_default_plaintext_header_size = 0;" lappend rc [string equal $salt "53514c69746520666f726d6174203300"] } {53514C69746520666F726D6174203300 {1 16} 0} db close file delete -force test.db file delete -force test2.db # migrate a standard database in place to use a # plaintext header offset by opening it, adjusting # the pragma, and rewriting the first page do_test test-plaintext-header-migrate-journal-delete { set rc {} sqlite_orig db test.db execsql " PRAGMA key = $hexkeyspec; CREATE TABLE t1(a,b); INSERT INTO t1(a,b) VALUES (1,2); " db close lappend rc [hexio_read test.db 0 16] sqlite_orig db test.db execsql " PRAGMA key = $hexkeyspec; SELECT count(*) FROM t1; PRAGMA cipher_plaintext_header_size = 32; PRAGMA user_version = 1; " db close lappend rc [hexio_read test.db 0 21] sqlite_orig db test.db lappend rc [execsql " PRAGMA key = $hexkeyspec; PRAGMA cipher_plaintext_header_size = 32; SELECT count(*) FROM t1; "] } {01010101010101010101010101010101 53514C69746520666F726D61742033001000010150 {ok 1}} db close file delete -force test.db # migrate a wal mode database in place to use a # plaintext header offset by opening it, adjusting # the pragma, and rewriting the first page do_test test-plaintext-header-migrate-journal-wal { set rc {} sqlite_orig db test.db execsql " PRAGMA key = $hexkeyspec; PRAGMA journal_mode = WAL; CREATE TABLE t1(a,b); INSERT INTO t1(a,b) VALUES (1,2); " db close lappend rc [hexio_read test.db 0 16] sqlite_orig db test.db lappend rc [execsql " PRAGMA key = $hexkeyspec; SELECT count(*) FROM t1; PRAGMA journal_mode; PRAGMA cipher_plaintext_header_size = 32; PRAGMA user_version = 1; PRAGMA wal_checkpoint(FULL); "] db close lappend rc [hexio_read test.db 0 21] sqlite_orig db test.db lappend rc [execsql " PRAGMA key = $hexkeyspec; PRAGMA cipher_plaintext_header_size = 32; SELECT count(*) FROM t1; PRAGMA journal_mode; "] } {01010101010101010101010101010101 {ok 1 wal 0 1 1} 53514C69746520666F726D61742033001000020250 {ok 1 wal}} db close file delete -force test.db # migrate a wal mode database in place to use a plaintext header # but instead of using a raw key syntax, use a derived key # but explicitly set the salt using cipher_salt do_test test-plaintext-header-migrate-journal-wal-string-key-random-salt { set rc {} sqlite_orig db test.db execsql { PRAGMA key = 'test'; PRAGMA journal_mode = WAL; CREATE TABLE t1(a,b); INSERT INTO t1(a,b) VALUES (1,2); } db close set salt [hexio_read test.db 0 16] sqlite_orig db test.db lappend rc [execsql " PRAGMA key = 'test'; SELECT count(*) FROM t1; PRAGMA journal_mode; PRAGMA cipher_plaintext_header_size = 32; PRAGMA user_version = 1; PRAGMA wal_checkpoint(FULL); "] db close lappend rc [hexio_read test.db 0 21] sqlite_orig db test.db lappend rc [execsql " PRAGMA key = 'test'; PRAGMA cipher_salt = \"x'$salt'\"; PRAGMA cipher_plaintext_header_size = 32; SELECT count(*) FROM t1; PRAGMA journal_mode; "] } {{ok 1 wal 0 1 1} 53514C69746520666F726D61742033001000020250 {ok 1 wal}} db close file delete -force test.db # when cipher_salt is the first statement a new salt should be generated # and it should match the salt after key derviation occurs. At no point # should the salt be zero do_test plaintext-header-size-salt-first-op { set rc {} sqlite_orig db test.db execsql { PRAGMA key = 'test'; } set salt1 [execsql { PRAGMA cipher_plaintext_header_size = 16; PRAGMA cipher_salt; }] set salt2 [execsql { CREATE TABLE t1(a,b); INSERT INTO t1(a,b) VALUES (1,2); PRAGMA cipher_salt; }] lappend rc [string equal $salt1 "00000000000000000000000000000000"] lappend rc [string equal $salt2 "00000000000000000000000000000000"] lappend rc [string equal $salt1 $salt2] } {0 0 1} db close file delete -force test.db finish_test