diff --git a/sqlcipher-2.0-beta-testkey.db b/sqlcipher-2.0-beta-testkey.db new file mode 100755 index 0000000..5db36bb Binary files /dev/null and b/sqlcipher-2.0-beta-testkey.db differ diff --git a/src/crypto.c b/src/crypto.c index 7a25f63..1e15968 100644 --- a/src/crypto.c +++ b/src/crypto.c @@ -163,6 +163,16 @@ int codec_pragma(sqlite3* db, int iDb, Parse *pParse, const char *zLeft, const c sqlcipher_codec_ctx_unset_flag(ctx, CIPHER_FLAG_LE_PGNO); sqlcipher_codec_ctx_unset_flag(ctx, CIPHER_FLAG_BE_PGNO); } + }else + if( sqlite3StrICmp(zLeft,"cipher_hmac_salt_mask")==0 ){ + if(zRight) { + if (sqlite3StrNICmp(zRight ,"x'", 2) == 0 && sqlite3Strlen30(zRight) == 5) { + unsigned char mask = 0; + const char *hex = zRight+2; + cipher_hex2bin(hex,2,&mask); + sqlcipher_set_hmac_salt_mask(mask); + } + } }else { return 0; } diff --git a/src/crypto.h b/src/crypto.h index 3dd400b..e44f82a 100644 --- a/src/crypto.h +++ b/src/crypto.h @@ -177,6 +177,7 @@ void* sqlcipher_codec_ctx_get_data(codec_ctx *); void sqlcipher_exportFunc(sqlite3_context *, int, sqlite3_value **); void sqlcipher_set_default_use_hmac(int use); +void sqlcipher_set_hmac_salt_mask(unsigned char mask); int sqlcipher_codec_ctx_set_use_hmac(codec_ctx *ctx, int use); diff --git a/src/crypto_impl.c b/src/crypto_impl.c index c8f5d51..e434c52 100644 --- a/src/crypto_impl.c +++ b/src/crypto_impl.c @@ -80,6 +80,7 @@ int sqlcipher_cipher_ctx_key_derive(codec_ctx *, cipher_ctx *); int sqlcipher_page_hmac(cipher_ctx *, Pgno, unsigned char *, int, unsigned char *); static unsigned int default_flags = DEFAULT_CIPHER_FLAGS; +static unsigned char hmac_salt_mask = HMAC_SALT_MASK; static unsigned int openssl_external_init = 0; static unsigned int openssl_init_count = 0; @@ -397,6 +398,10 @@ void sqlcipher_set_default_use_hmac(int use) { else default_flags &= ~CIPHER_FLAG_HMAC; } +void sqlcipher_set_hmac_salt_mask(unsigned char mask) { + hmac_salt_mask = mask; +} + int sqlcipher_get_default_use_hmac(Parse *pParse) { int default_use_hmac_set = default_flags & CIPHER_FLAG_HMAC > 0; char *default_use_hmac = sqlite3_mprintf("%d", default_use_hmac_set); @@ -749,7 +754,7 @@ int sqlcipher_cipher_ctx_key_derive(codec_ctx *ctx, cipher_ctx *c_ctx) { to generate the encryption key */ memcpy(ctx->hmac_kdf_salt, ctx->kdf_salt, ctx->kdf_salt_sz); for(i = 0; i < ctx->kdf_salt_sz; i++) { - ctx->hmac_kdf_salt[i] ^= HMAC_SALT_MASK; + ctx->hmac_kdf_salt[i] ^= hmac_salt_mask; } CODEC_TRACE(("codec_key_derive: deriving hmac key from encryption key using PBKDF2 with %d iterations\n", diff --git a/test/crypto.test b/test/crypto.test index 3ea63af..e077e1c 100644 --- a/test/crypto.test +++ b/test/crypto.test @@ -1649,6 +1649,10 @@ do_test verify-pragma-cipher-default-use-hmac-off { execsql { PRAGMA cipher_default_use_hmac = off; PRAGMA cipher_default_use_hmac; + -- Be sure to turn cipher_default_use_hmac + -- back on or it will break later tests + -- (it's a global flag) + PRAGMA cipher_default_use_hmac = ON; } } {0} db close @@ -1729,4 +1733,50 @@ do_test verify-pragma-cipher-changed { db close file delete -force test.db +# open a 2.0 beta database with 4000 round hmac kdf and 0x00 +# hmac salt mask +# verify it can be opened +do_test open-2.0-beta-database { + sqlite_orig db sqlcipher-2.0-beta-testkey.db + execsql { + PRAGMA key = 'testkey'; + PRAGMA fast_kdf_iter = 4000; + PRAGMA cipher_hmac_salt_mask = "x'00'"; + SELECT count(*) FROM t1; + SELECT * FROM t1; + } +} {2 test-0-0 test-0-1 test-1-0 test-1-1} +db close + +# open a 2.0 beta database +# attach a new standard database +# copy schema between the two, and verify the latter +# can be opened +do_test 2.0-beta-to-2.0-migration { + sqlite_orig db sqlcipher-2.0-beta-testkey.db + + execsql { + PRAGMA key = 'testkey'; + PRAGMA cipher_hmac_salt_mask = "x'00'"; + PRAGMA fast_kdf_iter = 4000; + SELECT count(*) FROM sqlite_master; + + PRAGMA cipher_hmac_salt_mask = "x'3a'"; + ATTACH DATABASE 'test.db' AS db2 KEY 'testkey'; + + CREATE TABLE db2.t1(a,b); + INSERT INTO db2.t1 SELECT * FROM main.t1; + DETACH DATABASE db2; + } + db close + + sqlite_orig db test.db + execsql { + PRAGMA key = 'testkey'; + SELECT * FROM t1; + } +} {test-0-0 test-0-1 test-1-0 test-1-1} +db close +file delete -force test.db + finish_test