diff --git a/CHANGELOG.md b/CHANGELOG.md index 888d1b6..c64588a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file. ## [4.2.0] - (May 2019 - [4.2.0 changes]) - Adds PRAGMA cipher_integrity_check to perform independent verification of page HMACs - Updates baseline to upstream SQLite 3.28.0 +- Improves PRAGMA cipher_migrate to handle keys containing non-terminating zero bytes ## [4.1.0] - (March 2019 - [4.1.0 changes]) - Defer reading salt from header until key derivation is triggered diff --git a/src/crypto_impl.c b/src/crypto_impl.c index d3a1f1e..112e2fc 100644 --- a/src/crypto_impl.c +++ b/src/crypto_impl.c @@ -1256,7 +1256,7 @@ cleanup: int sqlcipher_codec_ctx_integrity_check(codec_ctx *ctx, Parse *pParse, char *column) { Pgno page = 1; - int i, trans_rc, rc = 0; + int i, rc = 0; char *result; unsigned char *hmac_out = NULL; sqlite3_file *fd = sqlite3PagerFile(ctx->pBt->pBt->pPager); @@ -1284,13 +1284,6 @@ int sqlcipher_codec_ctx_integrity_check(codec_ctx *ctx, Parse *pParse, char *col goto cleanup; } - /* establish an exclusive lock on the database */ - if((trans_rc = sqlite3BtreeBeginTrans(ctx->pBt, 2, 0)) != SQLITE_OK) { - sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, "unable to lock database", P4_TRANSIENT); - sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); - goto cleanup; - } - sqlite3OsFileSize(fd, &file_sz); hmac_out = sqlcipher_malloc(ctx->hmac_sz); @@ -1330,7 +1323,6 @@ int sqlcipher_codec_ctx_integrity_check(codec_ctx *ctx, Parse *pParse, char *col } cleanup: - if(trans_rc == SQLITE_OK) sqlite3BtreeRollback(ctx->pBt, SQLITE_OK, 0); if(hmac_out != NULL) sqlcipher_free(hmac_out, ctx->hmac_sz); return SQLITE_OK; } @@ -1342,7 +1334,6 @@ int sqlcipher_codec_ctx_migrate(codec_ctx *ctx) { const char *db_filename = sqlite3_db_filename(db, "main"); char *set_user_version = NULL, *pass = NULL, *attach_command = NULL, *migrated_db_filename = NULL, *keyspec = NULL, *temp = NULL, *journal_mode = NULL, *set_journal_mode = NULL, *pragma_compat = NULL; Btree *pDest = NULL, *pSrc = NULL; - const char* commands[5]; sqlite3_file *srcfile, *destfile; #if defined(_WIN32) || defined(SQLITE_OS_WINRT) LPWSTR w_db_filename = NULL, w_migrated_db_filename = NULL; @@ -1389,23 +1380,46 @@ migrate: memcpy(migrated_db_filename, temp, sqlite3Strlen30(temp)); sqlcipher_free(temp, sqlite3Strlen30(temp)); - attach_command = sqlite3_mprintf("ATTACH DATABASE '%s' as migrate KEY '%q';", migrated_db_filename, pass); + attach_command = sqlite3_mprintf("ATTACH DATABASE '%s' as migrate;", migrated_db_filename, pass); set_user_version = sqlite3_mprintf("PRAGMA migrate.user_version = %d;", user_version); - commands[0] = pragma_compat; - commands[1] = "PRAGMA journal_mode = delete;"; /* force journal mode to DELETE, we will set it back later if different */ - commands[2] = attach_command; - commands[3] = "SELECT sqlcipher_export('migrate');"; - commands[4] = set_user_version; - - for(i = 0; i < ArraySize(commands); i++){ - rc = sqlite3_exec(db, commands[i], NULL, NULL, NULL); - if(rc != SQLITE_OK){ - CODEC_TRACE("migration step %d failed error code %d\n", i, rc); - goto handle_error; - } + rc = sqlite3_exec(db, pragma_compat, NULL, NULL, NULL); + if(rc != SQLITE_OK){ + CODEC_TRACE("set compatibility mode failed, error code %d\n", rc); + goto handle_error; } - + + /* force journal mode to DELETE, we will set it back later if different */ + rc = sqlite3_exec(db, "PRAGMA journal_mode = delete;", NULL, NULL, NULL); + if(rc != SQLITE_OK){ + CODEC_TRACE("force journal mode DELETE failed, error code %d\n", rc); + goto handle_error; + } + + rc = sqlite3_exec(db, attach_command, NULL, NULL, NULL); + if(rc != SQLITE_OK){ + CODEC_TRACE("attach failed, error code %d\n", rc); + goto handle_error; + } + + rc = sqlite3_key_v2(db, "migrate", pass, pass_sz); + if(rc != SQLITE_OK){ + CODEC_TRACE("keying attached database failed, error code %d\n", rc); + goto handle_error; + } + + rc = sqlite3_exec(db, "SELECT sqlcipher_export('migrate');", NULL, NULL, NULL); + if(rc != SQLITE_OK){ + CODEC_TRACE("sqlcipher_export failed, error code %d\n", rc); + goto handle_error; + } + + rc = sqlite3_exec(db, set_user_version, NULL, NULL, NULL); + if(rc != SQLITE_OK){ + CODEC_TRACE("set user version failed, error code %d\n", rc); + goto handle_error; + } + if( !db->autoCommit ){ CODEC_TRACE("cannot migrate from within a transaction"); goto handle_error; diff --git a/test/sqlcipher-integrity.test b/test/sqlcipher-integrity.test index ec6856b..098c162 100644 --- a/test/sqlcipher-integrity.test +++ b/test/sqlcipher-integrity.test @@ -308,25 +308,4 @@ do_test integrity-check-plaintext-header { } {{} 1 {{HMAC verification failed for page 1} {HMAC verification failed for page 2}}} file delete -force test.db -# verify database locking for cipher_integrity_check -do_test integrity-check-locking { - sqlite_orig db test.db - sqlite_orig db2 test.db - - execsql { - PRAGMA key = 'test'; - CREATE TABLE t1(a,b); - BEGIN EXCLUSIVE; - INSERT INTO t1(a,b) VALUES (1,2); - } - - execsql { - PRAGMA key = 'test'; - PRAGMA cipher_integrity_check; - } db2 -} {{unable to lock database}} -sqlite_orig db test.db -sqlite_orig db2 test.db -file delete -force test.db - finish_test