diff --git a/sqlcipher-4.0-testkey.db b/sqlcipher-4.0-testkey.db new file mode 100644 index 0000000..b8db537 Binary files /dev/null and b/sqlcipher-4.0-testkey.db differ diff --git a/src/crypto.c b/src/crypto.c index 5f89be7..fab3362 100644 --- a/src/crypto.c +++ b/src/crypto.c @@ -369,7 +369,6 @@ int sqlcipher_codec_pragma(sqlite3* db, int iDb, Parse *pParse, const char *zLef } else if(sqlite3StrICmp(zRight, SQLCIPHER_HMAC_SHA512_LABEL) == 0) { rc = sqlcipher_set_default_hmac_algorithm(SQLCIPHER_HMAC_SHA512); } - if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); } else { int algorithm = sqlcipher_get_default_hmac_algorithm(); if(algorithm == SQLCIPHER_HMAC_SHA1) { @@ -415,7 +414,6 @@ int sqlcipher_codec_pragma(sqlite3* db, int iDb, Parse *pParse, const char *zLef } else if(sqlite3StrICmp(zRight, SQLCIPHER_PBKDF2_HMAC_SHA512_LABEL) == 0) { rc = sqlcipher_set_default_kdf_algorithm(SQLCIPHER_PBKDF2_HMAC_SHA512); } - if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); } else { int algorithm = sqlcipher_get_default_kdf_algorithm(); if(algorithm == SQLCIPHER_PBKDF2_HMAC_SHA1) { @@ -427,6 +425,108 @@ int sqlcipher_codec_pragma(sqlite3* db, int iDb, Parse *pParse, const char *zLef } } }else + if( sqlite3StrICmp(zLeft,"cipher_compatibility")==0 ){ + if(ctx) { + if(zRight) { + int version = atoi(zRight); + + switch(version) { + case 1: + rc = sqlcipher_codec_ctx_set_pagesize(ctx, 1024); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = sqlcipher_codec_ctx_set_hmac_algorithm(ctx, SQLCIPHER_HMAC_SHA1); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = sqlcipher_codec_ctx_set_kdf_algorithm(ctx, SQLCIPHER_PBKDF2_HMAC_SHA1); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = sqlcipher_codec_ctx_set_kdf_iter(ctx, 4000); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = sqlcipher_codec_ctx_set_use_hmac(ctx, 0); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + break; + + case 2: + rc = sqlcipher_codec_ctx_set_pagesize(ctx, 1024); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = sqlcipher_codec_ctx_set_hmac_algorithm(ctx, SQLCIPHER_HMAC_SHA1); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = sqlcipher_codec_ctx_set_kdf_algorithm(ctx, SQLCIPHER_PBKDF2_HMAC_SHA1); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = sqlcipher_codec_ctx_set_kdf_iter(ctx, 4000); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = sqlcipher_codec_ctx_set_use_hmac(ctx, 1); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + break; + + case 3: + rc = sqlcipher_codec_ctx_set_pagesize(ctx, 1024); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = sqlcipher_codec_ctx_set_hmac_algorithm(ctx, SQLCIPHER_HMAC_SHA1); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = sqlcipher_codec_ctx_set_kdf_algorithm(ctx, SQLCIPHER_PBKDF2_HMAC_SHA1); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = sqlcipher_codec_ctx_set_kdf_iter(ctx, 64000); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = sqlcipher_codec_ctx_set_use_hmac(ctx, 1); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + break; + + default: + rc = sqlcipher_codec_ctx_set_pagesize(ctx, 4096); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = sqlcipher_codec_ctx_set_hmac_algorithm(ctx, SQLCIPHER_HMAC_SHA512); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = sqlcipher_codec_ctx_set_kdf_algorithm(ctx, SQLCIPHER_PBKDF2_HMAC_SHA512); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = sqlcipher_codec_ctx_set_kdf_iter(ctx, 256000); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + rc = sqlcipher_codec_ctx_set_use_hmac(ctx, 1); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + break; + } + + rc = codec_set_btree_to_codec_pagesize(db, pDb, ctx); + if (rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, SQLITE_ERROR); + } + } + }else + if( sqlite3StrICmp(zLeft,"cipher_default_compatibility")==0 ){ + if(zRight) { + int version = atoi(zRight); + switch(version) { + case 1: + sqlcipher_set_default_pagesize(1024); + sqlcipher_set_default_hmac_algorithm(SQLCIPHER_HMAC_SHA1); + sqlcipher_set_default_kdf_algorithm(SQLCIPHER_PBKDF2_HMAC_SHA1); + sqlcipher_set_default_kdf_iter(4000); + sqlcipher_set_default_use_hmac(0); + break; + + case 2: + sqlcipher_set_default_pagesize(1024); + sqlcipher_set_default_hmac_algorithm(SQLCIPHER_HMAC_SHA1); + sqlcipher_set_default_kdf_algorithm(SQLCIPHER_PBKDF2_HMAC_SHA1); + sqlcipher_set_default_kdf_iter(4000); + sqlcipher_set_default_use_hmac(1); + break; + + case 3: + sqlcipher_set_default_pagesize(1024); + sqlcipher_set_default_hmac_algorithm(SQLCIPHER_HMAC_SHA1); + sqlcipher_set_default_kdf_algorithm(SQLCIPHER_PBKDF2_HMAC_SHA1); + sqlcipher_set_default_kdf_iter(64000); + sqlcipher_set_default_use_hmac(1); + break; + + default: + sqlcipher_set_default_pagesize(4096); + sqlcipher_set_default_hmac_algorithm(SQLCIPHER_HMAC_SHA512); + sqlcipher_set_default_kdf_algorithm(SQLCIPHER_PBKDF2_HMAC_SHA512); + sqlcipher_set_default_kdf_iter(256000); + sqlcipher_set_default_use_hmac(1); + break; + } + } + }else if( sqlite3StrICmp(zLeft,"cipher_memory_security")==0 ){ if( zRight ) { sqlcipher_set_mem_security(sqlite3GetBoolean(zRight,1)); diff --git a/src/crypto_impl.c b/src/crypto_impl.c index 1538b9e..16a4eba 100644 --- a/src/crypto_impl.c +++ b/src/crypto_impl.c @@ -1235,14 +1235,11 @@ cleanup: } int sqlcipher_codec_ctx_migrate(codec_ctx *ctx) { - int i, pass_sz, keyspec_sz, nRes, user_version, upgrade_from, rc, oflags; + int i, pass_sz, keyspec_sz, nRes, user_version, rc, oflags; Db *pDb = 0; sqlite3 *db = ctx->pBt->db; const char *db_filename = sqlite3_db_filename(db, "main"); - char *v1_pragmas = "PRAGMA cipher_use_hmac = OFF; PRAGMA kdf_iter = 4000; PRAGMA cipher_page_size = 1024; PRAGMA cipher_hmac_algorithm = HMAC_SHA1; PRAGMA cipher_kdf_algorithm = PBKDF2_HMAC_SHA1;"; - char *v2_pragmas = "PRAGMA kdf_iter = 4000; PRAGMA cipher_page_size = 1024; PRAGMA cipher_hmac_algorithm = HMAC_SHA1; PRAGMA cipher_kdf_algorithm = PBKDF2_HMAC_SHA1;"; - char *v3_pragmas = "PRAGMA kdf_iter = 64000; PRAGMA cipher_page_size = 1024; PRAGMA cipher_hmac_algorithm = HMAC_SHA1; PRAGMA cipher_kdf_algorithm = PBKDF2_HMAC_SHA1;"; - 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; + 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; @@ -1250,7 +1247,7 @@ int sqlcipher_codec_ctx_migrate(codec_ctx *ctx) { LPWSTR w_db_filename = NULL, w_migrated_db_filename = NULL; int w_db_filename_sz = 0, w_migrated_db_filename_sz = 0; #endif - pass_sz = keyspec_sz = rc = user_version = upgrade_from = 0; + pass_sz = keyspec_sz = rc = user_version = 0; if(!db_filename || sqlite3Strlen30(db_filename) < 1) goto cleanup; /* exit immediately if this is an in memory database */ @@ -1268,29 +1265,19 @@ int sqlcipher_codec_ctx_migrate(codec_ctx *ctx) { goto cleanup; } - /* Version 3 - check for 64k with hmac format and 1024 page size */ - rc = sqlcipher_check_connection(db_filename, pass, pass_sz, v3_pragmas, &user_version, &journal_mode); - if(rc == SQLITE_OK) { - CODEC_TRACE("Version 3 format found\n"); - upgrade_from = 3; - goto migrate; - } - - /* Version 2 - check for 4k with hmac format and 1024 page size */ - rc = sqlcipher_check_connection(db_filename, pass, pass_sz, v2_pragmas, &user_version, &journal_mode); - if(rc == SQLITE_OK) { - CODEC_TRACE("Version 2 format found\n"); - upgrade_from = 2; - goto migrate; - } - - /* Version 1 - check no HMAC, 4k KDF, and 1024 page size */ - rc = sqlcipher_check_connection(db_filename, pass, pass_sz, v1_pragmas, &user_version, &journal_mode); - if(rc == SQLITE_OK) { - CODEC_TRACE("Version 1 format found\n"); - upgrade_from = 1; - goto migrate; + for(int i = 3; i > 0; i--) { + pragma_compat = sqlite3_mprintf("PRAGMA cipher_compatibility = %d;", i); + rc = sqlcipher_check_connection(db_filename, pass, pass_sz, pragma_compat, &user_version, &journal_mode); + if(rc == SQLITE_OK) { + CODEC_TRACE("Version %d format found\n", i); + goto migrate; + } + if(pragma_compat) sqlcipher_free(pragma_compat, sqlite3Strlen30(pragma_compat)); + pragma_compat = NULL; } + /* if we exit the loop normally we failed to determine the version, this is an error */ + CODEC_TRACE("Upgrade format not determined\n"); + goto handle_error; migrate: @@ -1303,20 +1290,8 @@ migrate: attach_command = sqlite3_mprintf("ATTACH DATABASE '%s' as migrate KEY '%q';", migrated_db_filename, pass); set_user_version = sqlite3_mprintf("PRAGMA migrate.user_version = %d;", user_version); - switch(upgrade_from) { - case 1: - commands[0] = v1_pragmas; - break; - case 2: - commands[0] = v2_pragmas; - break; - case 3: - commands[0] = v3_pragmas; - break; - default: - CODEC_TRACE("Upgrade format not determined\n"); - goto handle_error; - } + + 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');"; @@ -1423,6 +1398,7 @@ cleanup: if(set_user_version) sqlcipher_free(set_user_version, sqlite3Strlen30(set_user_version)); if(set_journal_mode) sqlcipher_free(set_journal_mode, sqlite3Strlen30(set_journal_mode)); if(journal_mode) sqlcipher_free(journal_mode, sqlite3Strlen30(journal_mode)); + if(pragma_compat) sqlcipher_free(pragma_compat, sqlite3Strlen30(pragma_compat)); #if defined(_WIN32) || defined(SQLITE_OS_WINRT) if(w_db_filename) sqlcipher_free(w_db_filename, w_db_filename_sz); if(w_migrated_db_filename) sqlcipher_free(w_migrated_db_filename, w_migrated_db_filename_sz); diff --git a/test/sqlcipher-compatibility.test b/test/sqlcipher-compatibility.test index a202751..5276231 100644 --- a/test/sqlcipher-compatibility.test +++ b/test/sqlcipher-compatibility.test @@ -107,6 +107,17 @@ db2 close file delete -force test.db file delete -force test2.db +# open a 4.0 database +do_test compat-open-4.0-database { + sqlite_orig db $sampleDir/sqlcipher-4.0-testkey.db + execsql { + PRAGMA key = 'testkey'; + PRAGMA integrity_check; + SELECT count(*) FROM t1; + } +} {ok 78536} +db close + # create an encrypted database, attach an default-key encrypted volume # copy data between, verify the second database do_test encrypted-attach-default-key { @@ -1228,6 +1239,99 @@ db close file delete -force plain.db file delete -force encrypted.db +# open a 1.1.8 database using cipher_compatibility +do_test compat-open-1.1.8-database { + sqlite_orig db $sampleDir/sqlcipher-1.1.8-testkey.db + execsql { + PRAGMA key = 'testkey'; + PRAGMA cipher_compatibility = 1; + PRAGMA integrity_check; + SELECT count(*) FROM t1; + } +} {ok 75709} +db close + +# open a 2.0 database using cipher_compatibility +do_test compat-open-2.0-database { + sqlite_orig db $sampleDir/sqlcipher-2.0-le-testkey.db + execsql { + PRAGMA key = 'testkey'; + PRAGMA cipher_compatibility = 2; + PRAGMA integrity_check; + SELECT count(*) FROM t1; + } +} {ok 78536} +db close + +# open a 3.0 database using cipher_compatibility +do_test compat-open-3.0-database { + sqlite_orig db $sampleDir/sqlcipher-3.0-testkey.db + execsql { + PRAGMA key = 'testkey'; + PRAGMA cipher_compatibility = 3; + PRAGMA integrity_check; + SELECT count(*) FROM t1; + } +} {ok 78536} +db close + +# open a 4.0 database using cipher_compatibility +do_test compat-open-4.0-database { + sqlite_orig db $sampleDir/sqlcipher-4.0-testkey.db + execsql { + PRAGMA key = 'testkey'; + PRAGMA cipher_compatibility = 4; + PRAGMA integrity_check; + SELECT count(*) FROM t1; + } +} {ok 78536} +db close + +# open a 1.1.8 database using cipher_default_compatibility +do_test default-compat-open-1.1.8-database { + sqlite_orig db $sampleDir/sqlcipher-1.1.8-testkey.db + execsql { + PRAGMA cipher_default_compatibility = 1; + PRAGMA key = 'testkey'; + PRAGMA integrity_check; + SELECT count(*) FROM t1; + } +} {ok 75709} +db close + +# open a 2.0 database using cipher_default_compatibility +do_test default-compat-open-2.0-database { + sqlite_orig db $sampleDir/sqlcipher-2.0-le-testkey.db + execsql { + PRAGMA cipher_default_compatibility = 2; + PRAGMA key = 'testkey'; + PRAGMA integrity_check; + SELECT count(*) FROM t1; + } +} {ok 78536} + +# open a 3.0 database using cipher_default_compatibility +do_test default-compat-open-3.0-database { + sqlite_orig db $sampleDir/sqlcipher-3.0-testkey.db + execsql { + PRAGMA cipher_default_compatibility = 3; + PRAGMA key = 'testkey'; + PRAGMA integrity_check; + SELECT count(*) FROM t1; + } +} {ok 78536} + +# re-open a 4.0 database using cipher_default_compatibility +do_test default-compat-open-4.0-database { + sqlite_orig db $sampleDir/sqlcipher-4.0-testkey.db + execsql { + PRAGMA cipher_default_compatibility = 4; + PRAGMA key = 'testkey'; + PRAGMA integrity_check; + SELECT count(*) FROM t1; + } +} {ok 78536} + sqlite3_test_control_pending_byte $old_pending_byte finish_test