mirror of
https://github.com/status-im/sqlcipher.git
synced 2025-02-23 01:08:09 +00:00
472 lines
14 KiB
Plaintext
472 lines
14 KiB
Plaintext
# 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
|