From 546f567f4e7e44f565315408756e77a94fe42082 Mon Sep 17 00:00:00 2001 From: Stephen Lombardo Date: Fri, 4 Jan 2019 12:03:21 -0500 Subject: [PATCH] defer reading salt from header until key derivation is triggered --- CHANGELOG.md | 1 + src/crypto.c | 2 +- src/crypto.h | 2 +- src/crypto_impl.c | 18 ++++++++------ test/sqlcipher-core.test | 54 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 67 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c125a37..10dde16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ All notable changes to this project will be documented in this file. ## [unreleased] - [unreleased] +- Defer reading salt from header until key derivation is triggered ## [4.0.1] - 2018-12-17 - Based on upstream SQLite 3.26.0 (addresses SQLite “Magellan” issue) diff --git a/src/crypto.c b/src/crypto.c index fab3362..6c027ca 100644 --- a/src/crypto.c +++ b/src/crypto.c @@ -651,7 +651,7 @@ int sqlite3CodecAttach(sqlite3* db, int nDb, const void *zKey, int nKey) { /* point the internal codec argument against the contet to be prepared */ CODEC_TRACE("sqlite3CodecAttach: calling sqlcipher_codec_ctx_init()\n"); - rc = sqlcipher_codec_ctx_init(&ctx, pDb, pDb->pBt->pBt->pPager, fd, zKey, nKey); + rc = sqlcipher_codec_ctx_init(&ctx, pDb, pDb->pBt->pBt->pPager, zKey, nKey); if(rc != SQLITE_OK) { /* initialization failed, do not attach potentially corrupted context */ diff --git a/src/crypto.h b/src/crypto.h index da64774..63f1121 100644 --- a/src/crypto.h +++ b/src/crypto.h @@ -205,7 +205,7 @@ void sqlcipher_init_memmethods(void); void sqlcipher_activate(void); void sqlcipher_deactivate(void); -int sqlcipher_codec_ctx_init(codec_ctx **, Db *, Pager *, sqlite3_file *, const void *, int); +int sqlcipher_codec_ctx_init(codec_ctx **, Db *, Pager *, const void *, int); void sqlcipher_codec_ctx_free(codec_ctx **); int sqlcipher_codec_key_derive(codec_ctx *); int sqlcipher_codec_key_copy(codec_ctx *, int); diff --git a/src/crypto_impl.c b/src/crypto_impl.c index c204293..84817cd 100644 --- a/src/crypto_impl.c +++ b/src/crypto_impl.c @@ -814,7 +814,7 @@ int sqlcipher_get_mem_security() { } -int sqlcipher_codec_ctx_init(codec_ctx **iCtx, Db *pDb, Pager *pPager, sqlite3_file *fd, const void *zKey, int nKey) { +int sqlcipher_codec_ctx_init(codec_ctx **iCtx, Db *pDb, Pager *pPager, const void *zKey, int nKey) { int rc; codec_ctx *ctx; @@ -846,11 +846,8 @@ int sqlcipher_codec_ctx_init(codec_ctx **iCtx, Db *pDb, Pager *pPager, sqlite3_f /* setup default flags */ ctx->flags = default_flags; - /* read salt from header, if present */ - CODEC_TRACE("sqlcipher_codec_ctx_init: reading file header\n"); - if(fd == NULL || sqlite3OsRead(fd, ctx->kdf_salt, ctx->kdf_salt_sz, 0) != SQLITE_OK) { - ctx->need_kdf_salt = 1; - } + /* defer attempt to read KDF salt until first use */ + ctx->need_kdf_salt = 1; /* setup the crypto provider */ CODEC_TRACE("sqlcipher_codec_ctx_init: allocating provider\n"); @@ -1094,9 +1091,14 @@ static int sqlcipher_cipher_ctx_key_derive(codec_ctx *ctx, cipher_ctx *c_ctx) { if(c_ctx->pass && c_ctx->pass_sz) { // if pass is not null - if(ctx->need_kdf_salt) { - if(ctx->provider->random(ctx->provider_ctx, ctx->kdf_salt, ctx->kdf_salt_sz) != SQLITE_OK) return SQLITE_ERROR; + sqlite3_file *fd = sqlite3PagerFile(ctx->pBt->pBt->pPager); + /* read salt from header, if present, otherwise generate a new random salt */ + CODEC_TRACE("sqlcipher_cipher_ctx_key_derive: obtaining salt\n"); + if(fd == NULL || fd->pMethods == 0 || sqlite3OsRead(fd, ctx->kdf_salt, ctx->kdf_salt_sz, 0) != SQLITE_OK) { + CODEC_TRACE("sqlcipher_cipher_ctx_key_derive: unable to read salt from file header, generating random\n"); + if(ctx->provider->random(ctx->provider_ctx, ctx->kdf_salt, ctx->kdf_salt_sz) != SQLITE_OK) return SQLITE_ERROR; + } ctx->need_kdf_salt = 0; } if (c_ctx->pass_sz == ((ctx->key_sz * 2) + 3) && sqlite3StrNICmp((const char *)c_ctx->pass ,"x'", 2) == 0 && cipher_isHex(c_ctx->pass + 2, ctx->key_sz * 2)) { diff --git a/test/sqlcipher-core.test b/test/sqlcipher-core.test index 9fbcdeb..d548c23 100644 --- a/test/sqlcipher-core.test +++ b/test/sqlcipher-core.test @@ -711,4 +711,58 @@ do_test verify-memory-security { db close file delete -force test.db +# create two new database files, write to each +# and verify that they have different (i.e. random) +# salt values +do_test test-random-salt { + sqlite_orig db test.db + sqlite_orig db2 test2.db + execsql { + PRAGMA key = 'test'; + CREATE TABLE t1(a,b); + INSERT INTO t1(a,b) VALUES (1,2); + } + execsql { + PRAGMA key = 'test'; + CREATE TABLE t1(a,b); + INSERT INTO t1(a,b) VALUES (1,2); + } db2 + db close + db2 close + string equal [hexio_read test.db 0 16] [hexio_read test2.db 0 16] +} {0} +file delete -force test.db +file delete -force test2.db + +# test scenario where multiple handles are opened +# to a file that does not exist, where both handles +# use the same key +do_test multiple-handles-same-key-and-salt { + sqlite_orig db test.db + sqlite_orig dba test.db + + execsql { + PRAGMA key = 'testkey'; + } + execsql { + PRAGMA key = 'testkey'; + } dba + + execsql { + CREATE TABLE t1(a,b); + INSERT INTO t1 VALUES(1,2); + } + + execsql { + SELECT count(*) FROM t1; + } + execsql { + SELECT count(*) FROM t1; + } dba + +} {1} +db close +dba close +file delete -force test.db + finish_test