mirror of
https://github.com/status-im/sqlcipher.git
synced 2025-02-22 08:48:10 +00:00
set error state if cipher_migrate fails
This commit is contained in:
parent
99d36b40c8
commit
78796fd0e2
49
src/crypto.c
49
src/crypto.c
@ -111,18 +111,39 @@ int sqlcipher_codec_pragma(sqlite3* db, int iDb, Parse *pParse, const char *zLef
|
||||
} else
|
||||
#endif
|
||||
#ifdef SQLCIPHER_TEST
|
||||
if( sqlite3StrICmp(zLeft,"cipher_test")==0 ){
|
||||
if( sqlite3StrICmp(zLeft,"cipher_test_on")==0 ){
|
||||
if( zRight ) {
|
||||
if(sqlite3StrICmp(zRight, "fail_next_encrypt")) {
|
||||
sqlcipher_set_test_flags(sqlcipher_get_test_flags() ^ TEST_FAIL_NEXT_ENCRYPT);
|
||||
unsigned int flags = sqlcipher_get_test_flags();
|
||||
if(sqlite3StrICmp(zRight, "fail_encrypt")==0) {
|
||||
flags |= TEST_FAIL_ENCRYPT;
|
||||
} else
|
||||
if(sqlite3StrICmp(zRight, "fail_next_decrypt")) {
|
||||
sqlcipher_set_test_flags(sqlcipher_get_test_flags() ^ TEST_FAIL_NEXT_DECRYPT);
|
||||
}
|
||||
} else {
|
||||
char *flags = sqlite3_mprintf("%d", sqlcipher_get_test_flags());
|
||||
codec_vdbe_return_string(pParse, "cipher_test", flags, P4_DYNAMIC);
|
||||
if(sqlite3StrICmp(zRight, "fail_decrypt")==0) {
|
||||
flags |= TEST_FAIL_DECRYPT;
|
||||
} else
|
||||
if(sqlite3StrICmp(zRight, "fail_migrate")==0) {
|
||||
flags |= TEST_FAIL_MIGRATE;
|
||||
}
|
||||
sqlcipher_set_test_flags(flags);
|
||||
}
|
||||
} else
|
||||
if( sqlite3StrICmp(zLeft,"cipher_test_off")==0 ){
|
||||
if( zRight ) {
|
||||
unsigned int flags = sqlcipher_get_test_flags();
|
||||
if(sqlite3StrICmp(zRight, "fail_encrypt")==0) {
|
||||
flags &= ~TEST_FAIL_ENCRYPT;
|
||||
} else
|
||||
if(sqlite3StrICmp(zRight, "fail_decrypt")==0) {
|
||||
flags &= ~TEST_FAIL_DECRYPT;
|
||||
} else
|
||||
if(sqlite3StrICmp(zRight, "fail_migrate")==0) {
|
||||
flags &= ~TEST_FAIL_MIGRATE;
|
||||
}
|
||||
sqlcipher_set_test_flags(flags);
|
||||
}
|
||||
} else
|
||||
if( sqlite3StrICmp(zLeft,"cipher_test")==0 ){
|
||||
char *flags = sqlite3_mprintf("%i", sqlcipher_get_test_flags());
|
||||
codec_vdbe_return_string(pParse, "cipher_test", flags, P4_DYNAMIC);
|
||||
}else
|
||||
#endif
|
||||
if( sqlite3StrICmp(zLeft, "cipher_fips_status")== 0 && !zRight ){
|
||||
@ -157,8 +178,12 @@ int sqlcipher_codec_pragma(sqlite3* db, int iDb, Parse *pParse, const char *zLef
|
||||
} else
|
||||
if( sqlite3StrICmp(zLeft, "cipher_migrate")==0 && !zRight ){
|
||||
if(ctx){
|
||||
char *migrate_status = sqlite3_mprintf("%d", sqlcipher_codec_ctx_migrate(ctx));
|
||||
int status = sqlcipher_codec_ctx_migrate(ctx);
|
||||
char *migrate_status = sqlite3_mprintf("%d", status);
|
||||
codec_vdbe_return_string(pParse, "cipher_migrate", migrate_status, P4_DYNAMIC);
|
||||
if(status != SQLITE_OK) {
|
||||
sqlcipher_codec_ctx_set_error(ctx, status);
|
||||
}
|
||||
}
|
||||
} else
|
||||
if( sqlite3StrICmp(zLeft, "cipher_provider")==0 && !zRight ){
|
||||
@ -709,7 +734,7 @@ static void* sqlite3Codec(void *iCtx, void *data, Pgno pgno, int mode) {
|
||||
|
||||
rc = sqlcipher_page_cipher(ctx, cctx, pgno, CIPHER_DECRYPT, page_sz - offset, pData + offset, (unsigned char*)buffer + offset);
|
||||
#ifdef SQLCIPHER_TEST
|
||||
if((sqlcipher_get_test_flags() & TEST_FAIL_NEXT_ENCRYPT) > 0) rc = SQLITE_ERROR;
|
||||
if((sqlcipher_get_test_flags() & TEST_FAIL_ENCRYPT) > 0) rc = SQLITE_ERROR;
|
||||
#endif
|
||||
if(rc != SQLITE_OK) { /* clear results of failed cipher operation and set error */
|
||||
sqlcipher_memset((unsigned char*) buffer+offset, 0, page_sz-offset);
|
||||
@ -734,7 +759,7 @@ static void* sqlite3Codec(void *iCtx, void *data, Pgno pgno, int mode) {
|
||||
}
|
||||
rc = sqlcipher_page_cipher(ctx, cctx, pgno, CIPHER_ENCRYPT, page_sz - offset, pData + offset, (unsigned char*)buffer + offset);
|
||||
#ifdef SQLCIPHER_TEST
|
||||
if((sqlcipher_get_test_flags() & TEST_FAIL_NEXT_DECRYPT) > 0) rc = SQLITE_ERROR;
|
||||
if((sqlcipher_get_test_flags() & TEST_FAIL_DECRYPT) > 0) rc = SQLITE_ERROR;
|
||||
#endif
|
||||
if(rc != SQLITE_OK) { /* clear results of failed cipher operation and set error */
|
||||
sqlcipher_memset((unsigned char*)buffer+offset, 0, page_sz-offset);
|
||||
|
@ -193,10 +193,11 @@ static int cipher_isHex(const unsigned char *hex, int sz){
|
||||
|
||||
/* possible flags for simulating specific test conditions */
|
||||
#ifdef SQLCIPHER_TEST
|
||||
#define TEST_FAIL_NEXT_ENCRYPT (1ul << 0) /* 1 */
|
||||
#define TEST_FAIL_NEXT_DECRYPT (1ul << 1) /* 2 */
|
||||
int sqlcipher_get_test_flags(void);
|
||||
void sqlcipher_set_test_flags(int);
|
||||
#define TEST_FAIL_ENCRYPT 0x01
|
||||
#define TEST_FAIL_DECRYPT 0x02
|
||||
#define TEST_FAIL_MIGRATE 0x04
|
||||
unsigned int sqlcipher_get_test_flags(void);
|
||||
void sqlcipher_set_test_flags(unsigned int);
|
||||
#endif
|
||||
|
||||
/* extensions defined in crypto_impl.c */
|
||||
|
@ -45,7 +45,13 @@
|
||||
#endif
|
||||
|
||||
#ifdef SQLCIPHER_TEST
|
||||
static volatile int cipher_test_flags = 0;
|
||||
static volatile unsigned int cipher_test_flags = 0;
|
||||
unsigned int sqlcipher_get_test_flags() {
|
||||
return cipher_test_flags;
|
||||
}
|
||||
void sqlcipher_set_test_flags(unsigned int flags) {
|
||||
cipher_test_flags = flags;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Generate code to return a string value */
|
||||
@ -402,16 +408,6 @@ char* sqlcipher_version() {
|
||||
return version;
|
||||
}
|
||||
|
||||
#ifdef SQLCIPHER_TEST
|
||||
int sqlcipher_get_test_flags() {
|
||||
return cipher_test_flags;
|
||||
}
|
||||
|
||||
void sqlcipher_set_test_flags(int flags) {
|
||||
cipher_test_flags = flags;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Initialize new cipher_ctx struct. This function will allocate memory
|
||||
* for the cipher context and for the key
|
||||
@ -1380,7 +1376,7 @@ int sqlcipher_codec_ctx_migrate(codec_ctx *ctx) {
|
||||
pass = sqlcipher_malloc(pass_sz+1);
|
||||
memset(pass, 0, pass_sz+1);
|
||||
memcpy(pass, ctx->read_ctx->pass, pass_sz);
|
||||
|
||||
|
||||
/* Version 4 - current, no upgrade required, so exit immediately */
|
||||
rc = sqlcipher_check_connection(db_filename, pass, pass_sz, "", &user_version, &journal_mode);
|
||||
if(rc == SQLITE_OK){
|
||||
@ -1398,6 +1394,7 @@ int sqlcipher_codec_ctx_migrate(codec_ctx *ctx) {
|
||||
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;
|
||||
@ -1445,6 +1442,14 @@ migrate:
|
||||
goto handle_error;
|
||||
}
|
||||
|
||||
#ifdef SQLCIPHER_TEST
|
||||
if((sqlcipher_get_test_flags() & TEST_FAIL_MIGRATE) > 0) {
|
||||
rc = SQLITE_ERROR;
|
||||
CODEC_TRACE("simulated migrate failure, error code %d\n", rc);
|
||||
goto handle_error;
|
||||
}
|
||||
#endif
|
||||
|
||||
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);
|
||||
@ -1537,7 +1542,6 @@ migrate:
|
||||
|
||||
handle_error:
|
||||
CODEC_TRACE("An error occurred attempting to migrate the database - last error %d\n", rc);
|
||||
rc = SQLITE_ERROR;
|
||||
|
||||
cleanup:
|
||||
if(pass) sqlcipher_free(pass, pass_sz);
|
||||
|
@ -66,7 +66,7 @@ do_test codec-error-journal-delete {
|
||||
|
||||
catchsql {
|
||||
PRAGMA key = 'testkey';
|
||||
PRAGMA cipher_test = fail_next_encrypt;
|
||||
PRAGMA cipher_test_on = fail_encrypt;
|
||||
UPDATE t1 SET b = 'fail' WHERE a = 5000;
|
||||
}
|
||||
|
||||
@ -74,7 +74,7 @@ do_test codec-error-journal-delete {
|
||||
sqlite_orig db test.db
|
||||
|
||||
execsql {
|
||||
PRAGMA cipher_test = fail_next_encrypt;
|
||||
PRAGMA cipher_test_off = fail_encrypt;
|
||||
PRAGMA key = 'testkey';
|
||||
PRAGMA cipher_integrity_check;
|
||||
PRAGMA integrity_check;
|
||||
@ -92,7 +92,7 @@ do_test codec-error-journal-wal {
|
||||
|
||||
catchsql {
|
||||
PRAGMA key = 'testkey';
|
||||
PRAGMA cipher_test = fail_next_encrypt;
|
||||
PRAGMA cipher_test_on = fail_encrypt;
|
||||
UPDATE t1 SET b = 'fail' WHERE a = 5000;
|
||||
}
|
||||
|
||||
@ -100,7 +100,7 @@ do_test codec-error-journal-wal {
|
||||
sqlite_orig db test.db
|
||||
|
||||
execsql {
|
||||
PRAGMA cipher_test = fail_next_encrypt;
|
||||
PRAGMA cipher_test_off = fail_encrypt;
|
||||
PRAGMA key = 'testkey';
|
||||
PRAGMA cipher_integrity_check;
|
||||
PRAGMA integrity_check;
|
||||
@ -120,7 +120,7 @@ do_test codec-error-journal-wal-transaction {
|
||||
PRAGMA key = 'testkey';
|
||||
BEGIN;
|
||||
UPDATE t1 SET b = 'success' WHERE a = 1;
|
||||
PRAGMA cipher_test = fail_next_encrypt;
|
||||
PRAGMA cipher_test_on = fail_encrypt;
|
||||
UPDATE t1 SET b = 'fail' WHERE a = 5000;
|
||||
COMMIT;
|
||||
}
|
||||
@ -129,7 +129,7 @@ do_test codec-error-journal-wal-transaction {
|
||||
sqlite_orig db test.db
|
||||
|
||||
execsql {
|
||||
PRAGMA cipher_test = fail_next_encrypt;
|
||||
PRAGMA cipher_test_off = fail_encrypt;
|
||||
PRAGMA key = 'testkey';
|
||||
PRAGMA cipher_integrity_check;
|
||||
PRAGMA integrity_check;
|
||||
@ -149,7 +149,7 @@ do_test codec-error-journal-wal-read {
|
||||
catchsql {
|
||||
PRAGMA key = 'testkey';
|
||||
SELECT count(*) FROM sqlite_schema;
|
||||
PRAGMA cipher_test = fail_next_decrypt;
|
||||
PRAGMA cipher_test_on = fail_decrypt;
|
||||
UPDATE t1 SET b = 'fail' WHERE a = 5000;
|
||||
}
|
||||
|
||||
@ -157,7 +157,7 @@ do_test codec-error-journal-wal-read {
|
||||
sqlite_orig db test.db
|
||||
|
||||
execsql {
|
||||
PRAGMA cipher_test = fail_next_decrypt;
|
||||
PRAGMA cipher_test_off = fail_decrypt;
|
||||
PRAGMA key = 'testkey';
|
||||
PRAGMA cipher_integrity_check;
|
||||
PRAGMA integrity_check;
|
||||
|
@ -360,7 +360,8 @@ file delete -force test.db
|
||||
|
||||
# open a 1.1.8 database without hmac, then copy the data
|
||||
do_test attach-and-copy-1.1.8 {
|
||||
sqlite_orig db $sampleDir/sqlcipher-1.1.8-testkey.db
|
||||
file copy -force $sampleDir/sqlcipher-1.1.8-testkey.db test.db
|
||||
sqlite_orig db test.db
|
||||
|
||||
execsql {
|
||||
PRAGMA key = 'testkey';
|
||||
@ -368,14 +369,14 @@ do_test attach-and-copy-1.1.8 {
|
||||
PRAGMA kdf_iter = 4000;
|
||||
PRAGMA cipher_page_size = 1024;
|
||||
PRAGMA cipher_kdf_algorithm = PBKDF2_HMAC_SHA1;
|
||||
ATTACH DATABASE 'test.db' AS db2 KEY 'testkey-hmac';
|
||||
ATTACH DATABASE 'test-new.db' AS db2 KEY 'testkey-hmac';
|
||||
CREATE TABLE db2.t1(a,b);
|
||||
INSERT INTO db2.t1 SELECT * FROM main.t1;
|
||||
DETACH DATABASE db2;
|
||||
}
|
||||
db close
|
||||
|
||||
sqlite_orig db test.db
|
||||
sqlite_orig db test-new.db
|
||||
execsql {
|
||||
PRAGMA key = 'testkey-hmac';
|
||||
SELECT count(*) FROM t1;
|
||||
@ -384,7 +385,7 @@ do_test attach-and-copy-1.1.8 {
|
||||
} {ok 75709 1 1 one one 1 2 one two 1 2}
|
||||
db close
|
||||
file delete -force test.db
|
||||
|
||||
file delete -force test-new.db
|
||||
|
||||
# open a standard database, then attach a new
|
||||
# database with completely different options.
|
||||
@ -681,7 +682,7 @@ file delete -force test-vacuum.db
|
||||
# setting as the original
|
||||
do_test default-hmac-kdf-attach {
|
||||
file copy -force $sampleDir/sqlcipher-1.1.8-testkey.db test.db
|
||||
file copy -force $sampleDir/sqlcipher-1.1.8-testkey.db sqlcipher-1.1.8-testkey.db;
|
||||
file copy -force $sampleDir/sqlcipher-1.1.8-testkey.db sqlcipher-1.1.8-testkey.db
|
||||
sqlite_orig db test.db
|
||||
execsql {
|
||||
PRAGMA cipher_default_use_hmac = OFF;
|
||||
@ -707,7 +708,7 @@ file delete -force sqlcipher-1.1.8-testkey.db
|
||||
# fail because the hmac setting for the
|
||||
# attached database is not compatible
|
||||
do_test attach-1.1.8-database-from-2.0-fails {
|
||||
file copy -force $sampleDir/sqlcipher-1.1.8-testkey.db sqlcipher-1.1.8-testkey.db;
|
||||
file copy -force $sampleDir/sqlcipher-1.1.8-testkey.db sqlcipher-1.1.8-testkey.db
|
||||
sqlite_orig db test.db
|
||||
catchsql {
|
||||
PRAGMA key = 'testkey';
|
||||
@ -725,7 +726,7 @@ file delete -force sqlcipher-1.1.8-testkey.db
|
||||
# succeed now that hmac is off by default
|
||||
# before the attach
|
||||
do_test change-default-hmac-kdf-attach {
|
||||
file copy -force $sampleDir/sqlcipher-1.1.8-testkey.db sqlcipher-1.1.8-testkey.db;
|
||||
file copy -force $sampleDir/sqlcipher-1.1.8-testkey.db sqlcipher-1.1.8-testkey.db
|
||||
sqlite_orig db test.db
|
||||
execsql {
|
||||
PRAGMA key = 'testkey';
|
||||
@ -1087,19 +1088,22 @@ file delete -force test.db test.db-migrated test.db-journal
|
||||
do_test migrate-3-0-database-to-current-format {
|
||||
file copy -force $sampleDir/sqlcipher-3.0-testkey.db test.db
|
||||
sqlite_orig db test.db
|
||||
execsql {
|
||||
set rc {}
|
||||
|
||||
lappend rc [execsql {
|
||||
PRAGMA key = 'testkey';
|
||||
PRAGMA cipher_migrate;
|
||||
}
|
||||
SELECT count(*) FROM sqlite_schema;
|
||||
}]
|
||||
db close
|
||||
|
||||
sqlite_orig db test.db
|
||||
execsql {
|
||||
lappend rc [execsql {
|
||||
PRAGMA key = 'testkey';
|
||||
SELECT count(*) FROM sqlite_schema;
|
||||
PRAGMA journal_mode;
|
||||
}
|
||||
} {ok 1 delete}
|
||||
}]
|
||||
} {{ok 0 1} {ok 1 delete}}
|
||||
db close
|
||||
file delete -force test.db
|
||||
|
||||
@ -1133,6 +1137,58 @@ do_test migrate-wal-database-to-current {
|
||||
db close
|
||||
file delete -force test.db
|
||||
|
||||
# test original database is left untouched after
|
||||
# a failed migration e.g. due to low disk space
|
||||
do_test migrate-failure {
|
||||
file copy -force $sampleDir/sqlcipher-3.0-testkey.db test.db
|
||||
sqlite_orig db test.db
|
||||
|
||||
set rc {}
|
||||
|
||||
lappend rc [execsql {
|
||||
PRAGMA key = 'testkey';
|
||||
PRAGMA cipher_test_on = fail_migrate;
|
||||
PRAGMA cipher_migrate;
|
||||
}]
|
||||
db close
|
||||
|
||||
sqlite_orig db test.db
|
||||
lappend rc [execsql {
|
||||
PRAGMA key = 'testkey';
|
||||
PRAGMA cipher_test_off = fail_migrate;
|
||||
PRAGMA cipher_compatibility = 3;
|
||||
SELECT count(*) FROM sqlite_schema;
|
||||
}]
|
||||
} {{ok 1} {ok 1}}
|
||||
db close
|
||||
file delete -force test.db test.db-migrated
|
||||
|
||||
# leave database is a readable state after a
|
||||
# a failed migration
|
||||
do_test migrate-failure-readable {
|
||||
file copy -force $sampleDir/sqlcipher-3.0-testkey.db test.db
|
||||
sqlite_orig db test.db
|
||||
|
||||
set rc {}
|
||||
lappend rc [execsql {
|
||||
PRAGMA key = 'testkey';
|
||||
PRAGMA cipher_test_on = fail_migrate;
|
||||
PRAGMA cipher_migrate;
|
||||
}]
|
||||
|
||||
lappend rc [catchsql {
|
||||
SELECT count(*) FROM sqlite_schema;
|
||||
}]
|
||||
db close
|
||||
|
||||
sqlite_orig db test.db
|
||||
lappend rc [execsql {
|
||||
PRAGMA cipher_test_off = fail_migrate;
|
||||
PRAGMA cipher_test;
|
||||
}]
|
||||
} {{ok 1} {1 {SQL logic error}} 0}
|
||||
db close
|
||||
file delete -force test.db test.db-migrated
|
||||
|
||||
do_test key-database-by-name {
|
||||
sqlite_orig db test.db
|
||||
@ -1259,6 +1315,7 @@ do_test can-migrate-with-raw-hex-key {
|
||||
PRAGMA key = "x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'";
|
||||
PRAGMA cipher_migrate;
|
||||
}
|
||||
db close
|
||||
|
||||
sqlite_orig db test.db
|
||||
execsql {
|
||||
|
@ -801,4 +801,54 @@ db close
|
||||
dba close
|
||||
file delete -force test.db
|
||||
|
||||
do_test test_flags_fail_encrypt {
|
||||
sqlite_orig db :memory:
|
||||
execsql {
|
||||
PRAGMA cipher_test;
|
||||
PRAGMA cipher_test_on = fail_encrypt;
|
||||
PRAGMA cipher_test;
|
||||
PRAGMA cipher_test_off = fail_encrypt;
|
||||
PRAGMA cipher_test;
|
||||
}
|
||||
} {0 1 0}
|
||||
db close
|
||||
|
||||
do_test test_flags_fail_decrypt {
|
||||
sqlite_orig db :memory:
|
||||
execsql {
|
||||
PRAGMA cipher_test;
|
||||
PRAGMA cipher_test_on = fail_decrypt;
|
||||
PRAGMA cipher_test;
|
||||
PRAGMA cipher_test_off = fail_decrypt;
|
||||
PRAGMA cipher_test;
|
||||
}
|
||||
} {0 2 0}
|
||||
db close
|
||||
|
||||
do_test test_flags_fail_migrate {
|
||||
sqlite_orig db :memory:
|
||||
execsql {
|
||||
PRAGMA cipher_test;
|
||||
PRAGMA cipher_test_on = fail_migrate;
|
||||
PRAGMA cipher_test;
|
||||
PRAGMA cipher_test_off = fail_migrate;
|
||||
PRAGMA cipher_test;
|
||||
}
|
||||
} {0 4 0}
|
||||
db close
|
||||
|
||||
do_test test_flags_combo {
|
||||
sqlite_orig db :memory:
|
||||
execsql {
|
||||
PRAGMA cipher_test;
|
||||
PRAGMA cipher_test_on = fail_encrypt;
|
||||
PRAGMA cipher_test_on = fail_migrate;
|
||||
PRAGMA cipher_test;
|
||||
PRAGMA cipher_test_off = fail_encrypt;
|
||||
PRAGMA cipher_test_off = fail_migrate;
|
||||
PRAGMA cipher_test;
|
||||
}
|
||||
} {0 5 0}
|
||||
db close
|
||||
|
||||
finish_test
|
||||
|
Loading…
x
Reference in New Issue
Block a user