From 1bb4737da2e9a1f0b912185d000bb377d7b7e8b4 Mon Sep 17 00:00:00 2001 From: Stephen Lombardo Date: Thu, 18 Jan 2018 10:22:12 -0500 Subject: [PATCH] adds PRAGMA default_cipher_plaintext_header_size and tests --- src/crypto.c | 9 +++++ src/crypto.h | 2 ++ src/crypto_impl.c | 25 +++++++++---- test/crypto.test | 89 ++++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 109 insertions(+), 16 deletions(-) diff --git a/src/crypto.c b/src/crypto.c index 7be7759..3f5d88e 100644 --- a/src/crypto.c +++ b/src/crypto.c @@ -302,6 +302,15 @@ int sqlcipher_codec_pragma(sqlite3* db, int iDb, Parse *pParse, const char *zLef } } }else + if( sqlite3StrICmp(zLeft,"cipher_default_plaintext_header_size")==0 ){ + if( zRight ) { + sqlcipher_set_default_plaintext_header_size(atoi(zRight)); + } else { + char *size = sqlite3_mprintf("%d", sqlcipher_get_default_plaintext_header_size()); + codec_vdbe_return_static_string(pParse, "cipher_default_plaintext_header_size", size); + sqlite3_free(size); + } + }else if( sqlite3StrICmp(zLeft,"cipher_salt")==0 ){ if(ctx) { if(zRight) { diff --git a/src/crypto.h b/src/crypto.h index ba90071..2913100 100644 --- a/src/crypto.h +++ b/src/crypto.h @@ -262,6 +262,8 @@ int sqlcipher_codec_hmac(const codec_ctx *ctx, const unsigned char *hmac_key, in unsigned char* in, int in_sz, unsigned char *in2, int in2_sz, unsigned char *out); +int sqlcipher_set_default_plaintext_header_size(int size); +int sqlcipher_get_default_plaintext_header_size(); int sqlcipher_codec_ctx_set_plaintext_header_size(codec_ctx *ctx, int size); int sqlcipher_codec_ctx_get_plaintext_header_size(codec_ctx *ctx); diff --git a/src/crypto_impl.c b/src/crypto_impl.c index f21b23f..99ff794 100644 --- a/src/crypto_impl.c +++ b/src/crypto_impl.c @@ -74,6 +74,7 @@ static unsigned int default_flags = DEFAULT_CIPHER_FLAGS; static unsigned char hmac_salt_mask = HMAC_SALT_MASK; static int default_kdf_iter = PBKDF2_ITER; static int default_page_size = 1024; +static int default_plaintext_header_sz = 0; static unsigned int sqlcipher_activate_count = 0; static sqlite3_mutex* sqlcipher_provider_mutex = NULL; static sqlcipher_provider *default_provider = NULL; @@ -686,12 +687,17 @@ int sqlcipher_codec_ctx_get_use_hmac(codec_ctx *ctx, int for_ctx) { return (c_ctx->flags & CIPHER_FLAG_HMAC) != 0; } +/* the lenght of plaintext header size must be: + * 1. greater than or equal to zero + * 2. a multiple of the cipher block size + * 3. less than the usable size of the first database page + */ +int sqlcipher_set_default_plaintext_header_size(int size) { + default_plaintext_header_sz = size; + return SQLITE_OK; +} + int sqlcipher_codec_ctx_set_plaintext_header_size(codec_ctx *ctx, int size) { - /* the lenght of plaintext header size must be: - * 1. greater than or equal to zero - * 2. a multiple of the cipher block size - * 3. less than the usable size of the first database page - */ if(size >= 0 && (size % ctx->read_ctx->block_sz) == 0 && size < (ctx->page_sz - ctx->read_ctx->reserve_sz)) { ctx->plaintext_header_sz = size; return SQLITE_OK; @@ -699,6 +705,10 @@ int sqlcipher_codec_ctx_set_plaintext_header_size(codec_ctx *ctx, int size) { return SQLITE_ERROR; } +int sqlcipher_get_default_plaintext_header_size() { + return default_plaintext_header_sz; +} + int sqlcipher_codec_ctx_get_plaintext_header_size(codec_ctx *ctx) { return ctx->plaintext_header_sz; } @@ -795,8 +805,6 @@ int sqlcipher_codec_ctx_init(codec_ctx **iCtx, Db *pDb, Pager *pPager, sqlite3_f ctx->pBt = pDb->pBt; /* assign pointer to database btree structure */ - ctx->plaintext_header_sz = 0; /* by default all data encrypted */ - /* allocate space for salt data. Then read the first 16 bytes directly off the database file. This is the salt for the key derivation function. If we get a short read allocate @@ -853,6 +861,9 @@ int sqlcipher_codec_ctx_init(codec_ctx **iCtx, Db *pDb, Pager *pPager, sqlite3_f CODEC_TRACE("sqlcipher_codec_ctx_init: copying write_ctx to read_ctx\n"); if((rc = sqlcipher_cipher_ctx_copy(ctx->write_ctx, ctx->read_ctx)) != SQLITE_OK) return rc; + CODEC_TRACE("sqlcipher_codec_ctx_init: calling sqlcipher_codec_ctx_set_plaintext_header_size with %d\n", default_plaintext_header_sz); + if((rc = sqlcipher_codec_ctx_set_plaintext_header_size(ctx, default_plaintext_header_sz)) != SQLITE_OK) return rc; + return SQLITE_OK; } diff --git a/test/crypto.test b/test/crypto.test index bd55262..62ea469 100644 --- a/test/crypto.test +++ b/test/crypto.test @@ -2346,7 +2346,6 @@ file delete -force encrypted.db # verify that a when a standard database is encrypted the first # 16 bytes are not "SQLite format 3\0" -# start of the database unencrypted, i.e. do_test test-sqlcipher-header-overwrite { sqlite_orig db test.db execsql { @@ -2373,8 +2372,7 @@ do_test test-pragma-salt-get { } {1} file delete -force test.db -# set the salt of a new database -# of an existing database +# explicitly set the salt of a new database do_test test-pragma-salt-set { set rc {} sqlite_orig db test.db @@ -2401,7 +2399,8 @@ file delete -force test.db set hexkeyspec "\"x'98483C6EB40B6C31A448C22A66DED3B5E5E8D5119CAC8327B655C8B5C483648101010101010101010101010101010101'\"" # verify that a raw key with a fixed salt will work -# first 16 bytes of database should be equal to the specified salt +# 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 {} @@ -2425,7 +2424,8 @@ do_test test-raw-key-with-salt-spec { db close file delete -force test.db -# verify that a raw key with an invalid salt will not work +# 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 @@ -2445,8 +2445,9 @@ do_test test-raw-key-with-invalid-salt-spec { db close file delete -force test.db -# verify that a raw key with a bad salt will work if page HMAC is disabled -# in which case the salt will not actually be used for anything +# 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 " @@ -2526,7 +2527,7 @@ 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 48 +# are readable and equal to 1024, 1, 1, and 48 respectively do_test test-plaintext-header-journal-delete-mode-readable { sqlite_orig db test.db execsql { @@ -2544,7 +2545,7 @@ 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 48 +# are readable and equal to 1024, 2, 2 and 48 respectively do_test test-plaintext-header-journal-wal-mode-readable { sqlite_orig db test.db execsql { @@ -2559,6 +2560,76 @@ do_test test-plaintext-header-journal-wal-mode-readable { } {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 + set salt [execsql { + PRAGMA cipher_default_plaintext_header_size = 16; + PRAGMA key = 'test'; + 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 + lappend rc [execsql " + PRAGMA key = 'test'; + 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 + set salt [execsql { + PRAGMA cipher_default_plaintext_header_size = 16; + PRAGMA key = 'test'; + 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 + lappend rc [execsql " + PRAGMA key = 'test'; + 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