sqlcipher/test/sqlcipher-plaintext-header.test

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 {out of memory}} {1 {out of memory}} {1 {out of memory}}}
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