Merge branch 'prerelease' into Xcode5
This commit is contained in:
commit
167aab2da6
Binary file not shown.
20
src/crypto.c
20
src/crypto.c
|
@ -89,6 +89,13 @@ int codec_pragma(sqlite3* db, int iDb, Parse *pParse, const char *zLeft, const c
|
|||
|
||||
CODEC_TRACE(("codec_pragma: entered db=%p iDb=%d pParse=%p zLeft=%s zRight=%s ctx=%p\n", db, iDb, pParse, zLeft, zRight, ctx));
|
||||
|
||||
if( sqlite3StrICmp(zLeft, "cipher_migrate")==0 && !zRight ){
|
||||
if(ctx){
|
||||
char *migrate_status = sqlite3_mprintf("%d", sqlcipher_codec_ctx_migrate(ctx));
|
||||
codec_vdbe_return_static_string(pParse, "sqlcipher_migrate", migrate_status);
|
||||
sqlite3_free(migrate_status);
|
||||
}
|
||||
} else
|
||||
if( sqlite3StrICmp(zLeft, "cipher_provider")==0 && !zRight ){
|
||||
if(ctx) { codec_vdbe_return_static_string(pParse, "cipher_provider",
|
||||
sqlcipher_codec_get_cipher_provider(ctx));
|
||||
|
@ -110,6 +117,15 @@ int codec_pragma(sqlite3* db, int iDb, Parse *pParse, const char *zLeft, const c
|
|||
if( sqlite3StrICmp(zLeft, "rekey_cipher")==0 && zRight ){
|
||||
if(ctx) sqlcipher_codec_ctx_set_cipher(ctx, zRight, 1); // change write cipher only
|
||||
}else
|
||||
if( sqlite3StrICmp(zLeft,"cipher_default_kdf_iter")==0 ){
|
||||
if( zRight ) {
|
||||
sqlcipher_set_default_kdf_iter(atoi(zRight)); // change default KDF iterations
|
||||
} else {
|
||||
char *kdf_iter = sqlite3_mprintf("%d", sqlcipher_get_default_kdf_iter());
|
||||
codec_vdbe_return_static_string(pParse, "cipher_default_kdf_iter", kdf_iter);
|
||||
sqlite3_free(kdf_iter);
|
||||
}
|
||||
}else
|
||||
if( sqlite3StrICmp(zLeft, "kdf_iter")==0 ){
|
||||
if(ctx) {
|
||||
if( zRight ) {
|
||||
|
@ -412,13 +428,11 @@ int sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey) {
|
|||
void sqlite3CodecGetKey(sqlite3* db, int nDb, void **zKey, int *nKey) {
|
||||
struct Db *pDb = &db->aDb[nDb];
|
||||
CODEC_TRACE(("sqlite3CodecGetKey: entered db=%p, nDb=%d\n", db, nDb));
|
||||
|
||||
if( pDb->pBt ) {
|
||||
codec_ctx *ctx;
|
||||
sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx);
|
||||
|
||||
if(ctx) { /* if the codec has an attached codec_context user the raw key data */
|
||||
sqlcipher_codec_get_pass(ctx, zKey, nKey);
|
||||
sqlcipher_codec_get_keyspec(ctx, zKey, nKey);
|
||||
} else {
|
||||
*zKey = NULL;
|
||||
*nKey = 0;
|
||||
|
|
17
src/crypto.h
17
src/crypto.h
|
@ -44,7 +44,7 @@
|
|||
#define FILE_HEADER_SZ 16
|
||||
|
||||
#ifndef CIPHER_VERSION
|
||||
#define CIPHER_VERSION "2.2.1"
|
||||
#define CIPHER_VERSION "3.0.0"
|
||||
#endif
|
||||
|
||||
#ifndef CIPHER
|
||||
|
@ -59,7 +59,7 @@
|
|||
#define CIPHER_READWRITE_CTX 2
|
||||
|
||||
#ifndef PBKDF2_ITER
|
||||
#define PBKDF2_ITER 4000
|
||||
#define PBKDF2_ITER 64000
|
||||
#endif
|
||||
|
||||
/* possible flags for cipher_ctx->flags */
|
||||
|
@ -149,6 +149,13 @@ static void cipher_hex2bin(const char *hex, int sz, unsigned char *out){
|
|||
}
|
||||
}
|
||||
|
||||
static void cipher_bin2hex(const unsigned char* in, int sz, char *out) {
|
||||
int i;
|
||||
for(i=0; i < sz; i++) {
|
||||
sprintf(out + (i*2), "%02x ", in[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* extensions defined in crypto_impl.c */
|
||||
typedef struct codec_ctx codec_ctx;
|
||||
|
||||
|
@ -167,12 +174,15 @@ int sqlcipher_page_cipher(codec_ctx *, int, Pgno, int, int, unsigned char *, uns
|
|||
void sqlcipher_codec_ctx_set_error(codec_ctx *, int);
|
||||
|
||||
int sqlcipher_codec_ctx_set_pass(codec_ctx *, const void *, int, int);
|
||||
void sqlcipher_codec_get_pass(codec_ctx *, void **zKey, int *nKey);
|
||||
void sqlcipher_codec_get_keyspec(codec_ctx *, void **zKey, int *nKey);
|
||||
|
||||
int sqlcipher_codec_ctx_set_pagesize(codec_ctx *, int);
|
||||
int sqlcipher_codec_ctx_get_pagesize(codec_ctx *);
|
||||
int sqlcipher_codec_ctx_get_reservesize(codec_ctx *);
|
||||
|
||||
void sqlcipher_set_default_kdf_iter(int iter);
|
||||
int sqlcipher_get_default_kdf_iter();
|
||||
|
||||
int sqlcipher_codec_ctx_set_kdf_iter(codec_ctx *, int, int);
|
||||
int sqlcipher_codec_ctx_get_kdf_iter(codec_ctx *ctx, int);
|
||||
|
||||
|
@ -202,6 +212,7 @@ int sqlcipher_codec_ctx_unset_flag(codec_ctx *ctx, unsigned int flag);
|
|||
int sqlcipher_codec_ctx_get_flag(codec_ctx *ctx, unsigned int flag, int for_ctx);
|
||||
|
||||
const char* sqlcipher_codec_get_cipher_provider(codec_ctx *ctx);
|
||||
int sqlcipher_codec_ctx_migrate(codec_ctx *ctx);
|
||||
#endif
|
||||
#endif
|
||||
/* END SQLCIPHER */
|
||||
|
|
|
@ -56,16 +56,19 @@ typedef struct {
|
|||
int pass_sz;
|
||||
int reserve_sz;
|
||||
int hmac_sz;
|
||||
int keyspec_sz;
|
||||
unsigned int flags;
|
||||
unsigned char *key;
|
||||
unsigned char *hmac_key;
|
||||
char *pass;
|
||||
char *keyspec;
|
||||
sqlcipher_provider *provider;
|
||||
void *provider_ctx;
|
||||
} cipher_ctx;
|
||||
|
||||
static unsigned int default_flags = DEFAULT_CIPHER_FLAGS;
|
||||
static unsigned char hmac_salt_mask = HMAC_SALT_MASK;
|
||||
static int default_kdf_iter = PBKDF2_ITER;
|
||||
static unsigned int sqlcipher_activate_count = 0;
|
||||
static sqlite3_mutex* sqlcipher_provider_mutex = NULL;
|
||||
static sqlcipher_provider *default_provider = NULL;
|
||||
|
@ -289,6 +292,7 @@ static void sqlcipher_cipher_ctx_free(cipher_ctx **iCtx) {
|
|||
sqlcipher_free(ctx->key, ctx->key_sz);
|
||||
sqlcipher_free(ctx->hmac_key, ctx->key_sz);
|
||||
sqlcipher_free(ctx->pass, ctx->pass_sz);
|
||||
sqlcipher_free(ctx->keyspec, ctx->keyspec_sz);
|
||||
sqlcipher_free(ctx, sizeof(cipher_ctx));
|
||||
}
|
||||
|
||||
|
@ -336,8 +340,9 @@ static int sqlcipher_cipher_ctx_copy(cipher_ctx *target, cipher_ctx *source) {
|
|||
|
||||
CODEC_TRACE(("sqlcipher_cipher_ctx_copy: entered target=%p, source=%p\n", target, source));
|
||||
sqlcipher_free(target->pass, target->pass_sz);
|
||||
sqlcipher_free(target->keyspec, target->keyspec_sz);
|
||||
memcpy(target, source, sizeof(cipher_ctx));
|
||||
|
||||
|
||||
target->key = key; //restore pointer to previously allocated key data
|
||||
memcpy(target->key, source->key, CIPHER_MAX_KEY_SZ);
|
||||
|
||||
|
@ -350,31 +355,67 @@ static int sqlcipher_cipher_ctx_copy(cipher_ctx *target, cipher_ctx *source) {
|
|||
target->provider_ctx = provider_ctx; // restore pointer to previouly allocated provider context;
|
||||
target->provider->ctx_copy(target->provider_ctx, source->provider_ctx);
|
||||
|
||||
target->pass = sqlcipher_malloc(source->pass_sz);
|
||||
if(target->pass == NULL) return SQLITE_NOMEM;
|
||||
memcpy(target->pass, source->pass, source->pass_sz);
|
||||
if(source->pass && source->pass_sz) {
|
||||
target->pass = sqlcipher_malloc(source->pass_sz);
|
||||
if(target->pass == NULL) return SQLITE_NOMEM;
|
||||
memcpy(target->pass, source->pass, source->pass_sz);
|
||||
}
|
||||
if(source->keyspec && source->keyspec_sz) {
|
||||
target->keyspec = sqlcipher_malloc(source->keyspec_sz);
|
||||
if(target->keyspec == NULL) return SQLITE_NOMEM;
|
||||
memcpy(target->keyspec, source->keyspec, source->keyspec_sz);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the keyspec for the cipher_ctx
|
||||
*
|
||||
* returns SQLITE_OK if assignment was successfull
|
||||
* returns SQLITE_NOMEM if an error occured allocating memory
|
||||
*/
|
||||
static int sqlcipher_cipher_ctx_set_keyspec(cipher_ctx *ctx, const unsigned char *key, int key_sz, const unsigned char *salt, int salt_sz) {
|
||||
|
||||
/* free, zero existing pointers and size */
|
||||
sqlcipher_free(ctx->keyspec, ctx->keyspec_sz);
|
||||
ctx->keyspec = NULL;
|
||||
ctx->keyspec_sz = 0;
|
||||
|
||||
/* establic a hex-formated key specification, containing the raw encryption key and
|
||||
the salt used to generate it */
|
||||
ctx->keyspec_sz = ((key_sz + salt_sz) * 2) + 3;
|
||||
ctx->keyspec = sqlcipher_malloc(ctx->keyspec_sz);
|
||||
if(ctx->keyspec == NULL) return SQLITE_NOMEM;
|
||||
|
||||
ctx->keyspec[0] = 'x';
|
||||
ctx->keyspec[1] = '\'';
|
||||
ctx->keyspec[ctx->keyspec_sz - 1] = '\'';
|
||||
cipher_bin2hex(key, key_sz, ctx->keyspec + 2);
|
||||
cipher_bin2hex(salt, salt_sz, ctx->keyspec + (key_sz * 2) + 2);
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the raw password / key data for a cipher context
|
||||
* Set the passphrase for the cipher_ctx
|
||||
*
|
||||
* returns SQLITE_OK if assignment was successfull
|
||||
* returns SQLITE_NOMEM if an error occured allocating memory
|
||||
* returns SQLITE_ERROR if the key couldn't be set because the pass was null or size was zero
|
||||
*/
|
||||
static int sqlcipher_cipher_ctx_set_pass(cipher_ctx *ctx, const void *zKey, int nKey) {
|
||||
|
||||
/* free, zero existing pointers and size */
|
||||
sqlcipher_free(ctx->pass, ctx->pass_sz);
|
||||
ctx->pass_sz = nKey;
|
||||
if(zKey && nKey) {
|
||||
ctx->pass = NULL;
|
||||
ctx->pass_sz = 0;
|
||||
|
||||
if(zKey && nKey) { /* if new password is provided, copy it */
|
||||
ctx->pass_sz = nKey;
|
||||
ctx->pass = sqlcipher_malloc(nKey);
|
||||
if(ctx->pass == NULL) return SQLITE_NOMEM;
|
||||
memcpy(ctx->pass, zKey, nKey);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
int sqlcipher_codec_ctx_set_pass(codec_ctx *ctx, const void *zKey, int nKey, int for_ctx) {
|
||||
|
@ -415,6 +456,15 @@ const char* sqlcipher_codec_ctx_get_cipher(codec_ctx *ctx, int for_ctx) {
|
|||
return c_ctx->provider->get_cipher(c_ctx->provider_ctx);
|
||||
}
|
||||
|
||||
/* set the global default KDF iteration */
|
||||
void sqlcipher_set_default_kdf_iter(int iter) {
|
||||
default_kdf_iter = iter;
|
||||
}
|
||||
|
||||
int sqlcipher_get_default_kdf_iter() {
|
||||
return default_kdf_iter;
|
||||
}
|
||||
|
||||
int sqlcipher_codec_ctx_set_kdf_iter(codec_ctx *ctx, int kdf_iter, int for_ctx) {
|
||||
cipher_ctx *c_ctx = for_ctx ? ctx->write_ctx : ctx->read_ctx;
|
||||
int rc;
|
||||
|
@ -537,9 +587,9 @@ void* sqlcipher_codec_ctx_get_kdf_salt(codec_ctx *ctx) {
|
|||
return ctx->kdf_salt;
|
||||
}
|
||||
|
||||
void sqlcipher_codec_get_pass(codec_ctx *ctx, void **zKey, int *nKey) {
|
||||
*zKey = ctx->read_ctx->pass;
|
||||
*nKey = ctx->read_ctx->pass_sz;
|
||||
void sqlcipher_codec_get_keyspec(codec_ctx *ctx, void **zKey, int *nKey) {
|
||||
*zKey = ctx->read_ctx->keyspec;
|
||||
*nKey = ctx->read_ctx->keyspec_sz;
|
||||
}
|
||||
|
||||
int sqlcipher_codec_ctx_set_pagesize(codec_ctx *ctx, int size) {
|
||||
|
@ -601,7 +651,7 @@ int sqlcipher_codec_ctx_init(codec_ctx **iCtx, Db *pDb, Pager *pPager, sqlite3_f
|
|||
}
|
||||
|
||||
if((rc = sqlcipher_codec_ctx_set_cipher(ctx, CIPHER, 0)) != SQLITE_OK) return rc;
|
||||
if((rc = sqlcipher_codec_ctx_set_kdf_iter(ctx, PBKDF2_ITER, 0)) != SQLITE_OK) return rc;
|
||||
if((rc = sqlcipher_codec_ctx_set_kdf_iter(ctx, default_kdf_iter, 0)) != SQLITE_OK) return rc;
|
||||
if((rc = sqlcipher_codec_ctx_set_fast_kdf_iter(ctx, FAST_PBKDF2_ITER, 0)) != SQLITE_OK) return rc;
|
||||
if((rc = sqlcipher_codec_ctx_set_pass(ctx, zKey, nKey, 0)) != SQLITE_OK) return rc;
|
||||
|
||||
|
@ -750,7 +800,11 @@ int sqlcipher_page_cipher(codec_ctx *ctx, int for_ctx, Pgno pgno, int mode, int
|
|||
* Derive an encryption key for a cipher contex key based on the raw password.
|
||||
*
|
||||
* If the raw key data is formated as x'hex' and there are exactly enough hex chars to fill
|
||||
* the key space (i.e 64 hex chars for a 256 bit key) then the key data will be used directly.
|
||||
* the key (i.e 64 hex chars for a 256 bit key) then the key data will be used directly.
|
||||
|
||||
* Else, if the raw key data is formated as x'hex' and there are exactly enough hex chars to fill
|
||||
* the key and the salt (i.e 92 hex chars for a 256 bit key and 16 byte salt) then it will be unpacked
|
||||
* as the key followed by the salt.
|
||||
*
|
||||
* Otherwise, a key data will be derived using PBKDF2
|
||||
*
|
||||
|
@ -758,7 +812,8 @@ int sqlcipher_page_cipher(codec_ctx *ctx, int for_ctx, Pgno pgno, int mode, int
|
|||
* returns SQLITE_ERROR if the key could't be derived (for instance if pass is NULL or pass_sz is 0)
|
||||
*/
|
||||
static int sqlcipher_cipher_ctx_key_derive(codec_ctx *ctx, cipher_ctx *c_ctx) {
|
||||
CODEC_TRACE(("codec_key_derive: entered c_ctx->pass=%s, c_ctx->pass_sz=%d \
|
||||
int rc;
|
||||
CODEC_TRACE(("cipher_ctx_key_derive: entered c_ctx->pass=%s, c_ctx->pass_sz=%d \
|
||||
ctx->kdf_salt=%p ctx->kdf_salt_sz=%d c_ctx->kdf_iter=%d \
|
||||
ctx->hmac_kdf_salt=%p, c_ctx->fast_kdf_iter=%d c_ctx->key_sz=%d\n",
|
||||
c_ctx->pass, c_ctx->pass_sz, ctx->kdf_salt, ctx->kdf_salt_sz, c_ctx->kdf_iter,
|
||||
|
@ -766,19 +821,26 @@ 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 (c_ctx->pass_sz == ((c_ctx->key_sz*2)+3) && sqlite3StrNICmp(c_ctx->pass ,"x'", 2) == 0) {
|
||||
if (c_ctx->pass_sz == ((c_ctx->key_sz * 2) + 3) && sqlite3StrNICmp(c_ctx->pass ,"x'", 2) == 0) {
|
||||
int n = c_ctx->pass_sz - 3; /* adjust for leading x' and tailing ' */
|
||||
const char *z = c_ctx->pass + 2; /* adjust lead offset of x' */
|
||||
CODEC_TRACE(("codec_key_derive: using raw key from hex\n"));
|
||||
CODEC_TRACE(("cipher_ctx_key_derive: using raw key from hex\n"));
|
||||
cipher_hex2bin(z, n, c_ctx->key);
|
||||
} else if (c_ctx->pass_sz == (((c_ctx->key_sz + ctx->kdf_salt_sz) * 2) + 3) && sqlite3StrNICmp(c_ctx->pass ,"x'", 2) == 0) {
|
||||
const char *z = c_ctx->pass + 2; /* adjust lead offset of x' */
|
||||
CODEC_TRACE(("cipher_ctx_key_derive: using raw key from hex\n"));
|
||||
cipher_hex2bin(z, (c_ctx->key_sz * 2), c_ctx->key);
|
||||
cipher_hex2bin(z + (c_ctx->key_sz * 2), (ctx->kdf_salt_sz * 2), ctx->kdf_salt);
|
||||
} else {
|
||||
CODEC_TRACE(("codec_key_derive: deriving key using full PBKDF2 with %d iterations\n", c_ctx->kdf_iter));
|
||||
CODEC_TRACE(("cipher_ctx_key_derive: deriving key using full PBKDF2 with %d iterations\n", c_ctx->kdf_iter));
|
||||
c_ctx->provider->kdf(c_ctx->provider_ctx, (const char*) c_ctx->pass, c_ctx->pass_sz,
|
||||
ctx->kdf_salt, ctx->kdf_salt_sz, c_ctx->kdf_iter,
|
||||
c_ctx->key_sz, c_ctx->key);
|
||||
|
||||
}
|
||||
|
||||
/* set the context "keyspec" containing the hex-formatted key and salt to be used when attaching databases */
|
||||
if((rc = sqlcipher_cipher_ctx_set_keyspec(c_ctx, c_ctx->key, c_ctx->key_sz, ctx->kdf_salt, ctx->kdf_salt_sz)) != SQLITE_OK) return rc;
|
||||
|
||||
/* if this context is setup to use hmac checks, generate a seperate and different
|
||||
key for HMAC. In this case, we use the output of the previous KDF as the input to
|
||||
this KDF run. This ensures a distinct but predictable HMAC key. */
|
||||
|
@ -795,7 +857,7 @@ static int sqlcipher_cipher_ctx_key_derive(codec_ctx *ctx, cipher_ctx *c_ctx) {
|
|||
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",
|
||||
CODEC_TRACE(("cipher_ctx_key_derive: deriving hmac key from encryption key using PBKDF2 with %d iterations\n",
|
||||
c_ctx->fast_kdf_iter));
|
||||
|
||||
|
||||
|
@ -818,12 +880,17 @@ int sqlcipher_codec_key_derive(codec_ctx *ctx) {
|
|||
|
||||
if(ctx->write_ctx->derive_key) {
|
||||
if(sqlcipher_cipher_ctx_cmp(ctx->write_ctx, ctx->read_ctx) == 0) {
|
||||
// the relevant parameters are the same, just copy read key
|
||||
/* the relevant parameters are the same, just copy read key */
|
||||
if(sqlcipher_cipher_ctx_copy(ctx->write_ctx, ctx->read_ctx) != SQLITE_OK) return SQLITE_ERROR;
|
||||
} else {
|
||||
if(sqlcipher_cipher_ctx_key_derive(ctx, ctx->write_ctx) != SQLITE_OK) return SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: wipe and free passphrase after key derivation */
|
||||
sqlcipher_cipher_ctx_set_pass(ctx->read_ctx, NULL, 0);
|
||||
sqlcipher_cipher_ctx_set_pass(ctx->write_ctx, NULL, 0);
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
@ -839,5 +906,208 @@ const char* sqlcipher_codec_get_cipher_provider(codec_ctx *ctx) {
|
|||
return ctx->read_ctx->provider->get_provider_name(ctx->read_ctx);
|
||||
}
|
||||
|
||||
int sqlcipher_codec_ctx_migrate(codec_ctx *ctx) {
|
||||
u32 meta;
|
||||
int rc = 0;
|
||||
int command_idx = 0;
|
||||
int password_sz;
|
||||
int saved_flags;
|
||||
int saved_nChange;
|
||||
int saved_nTotalChange;
|
||||
void (*saved_xTrace)(void*,const char*);
|
||||
Db *pDb = 0;
|
||||
sqlite3 *db = ctx->pBt->db;
|
||||
const char *db_filename = sqlite3_db_filename(db, "main");
|
||||
char *migrated_db_filename = sqlite3_mprintf("%s-migrated", db_filename);
|
||||
char *query_sqlite_master = "SELECT count(*) from sqlite_master;";
|
||||
char *pragma_hmac_off = "PRAGMA cipher_use_hmac = OFF;";
|
||||
char *pragma_4k_kdf_iter = "PRAGMA kdf_iter = 4000;";
|
||||
char *key;
|
||||
int key_sz;
|
||||
int upgrade_1x_format = 0;
|
||||
int upgrade_4k_format = 0;
|
||||
sqlite3 *test;
|
||||
char *err = 0;
|
||||
static const unsigned char aCopy[] = {
|
||||
BTREE_SCHEMA_VERSION, 1, /* Add one to the old schema cookie */
|
||||
BTREE_DEFAULT_CACHE_SIZE, 0, /* Preserve the default page cache size */
|
||||
BTREE_TEXT_ENCODING, 0, /* Preserve the text encoding */
|
||||
BTREE_USER_VERSION, 0, /* Preserve the user version */
|
||||
BTREE_APPLICATION_ID, 0, /* Preserve the application id */
|
||||
};
|
||||
key_sz = ctx->read_ctx->pass_sz + 1;
|
||||
key = sqlcipher_malloc(key_sz);
|
||||
memset(key, 0, key_sz);
|
||||
memcpy(key, ctx->read_ctx->pass, ctx->read_ctx->pass_sz);
|
||||
|
||||
if(db_filename){
|
||||
|
||||
char *attach_command = sqlite3_mprintf("ATTACH DATABASE '%s-migrated' as migrate KEY '%s';",
|
||||
db_filename, key);
|
||||
|
||||
int rc = sqlcipher_check_connection(db_filename, key, key_sz, "");
|
||||
if(rc == SQLITE_OK){
|
||||
CODEC_TRACE(("No upgrade required - exiting\n"));
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// Version 2 - check for 4k with hmac format
|
||||
rc = sqlcipher_check_connection(db_filename, key, key_sz, pragma_4k_kdf_iter);
|
||||
if(rc == SQLITE_OK) {
|
||||
CODEC_TRACE(("Version 2 format found\n"));
|
||||
upgrade_4k_format = 1;
|
||||
}
|
||||
|
||||
// Version 1 - check both no hmac and 4k together
|
||||
char *pragma_1x_and_4k = sqlite3_mprintf("%s%s", pragma_hmac_off,
|
||||
pragma_4k_kdf_iter);
|
||||
rc = sqlcipher_check_connection(db_filename, key, key_sz, pragma_1x_and_4k);
|
||||
sqlite3_free(pragma_1x_and_4k);
|
||||
if(rc == SQLITE_OK) {
|
||||
CODEC_TRACE(("Version 1 format found\n"));
|
||||
upgrade_1x_format = 1;
|
||||
upgrade_4k_format = 1;
|
||||
}
|
||||
|
||||
if(upgrade_1x_format == 0 && upgrade_4k_format == 0) {
|
||||
CODEC_TRACE(("Upgrade format not determined\n"));
|
||||
goto handle_error;
|
||||
}
|
||||
|
||||
const char *commands[] = {
|
||||
upgrade_4k_format == 1 ? pragma_4k_kdf_iter : "",
|
||||
upgrade_1x_format == 1 ? pragma_hmac_off : "",
|
||||
attach_command,
|
||||
"SELECT sqlcipher_export('migrate');",
|
||||
};
|
||||
for(command_idx = 0; command_idx < ArraySize(commands); command_idx++){
|
||||
const char *command = commands[command_idx];
|
||||
if(strcmp(command, "") == 0){
|
||||
continue;
|
||||
}
|
||||
rc = sqlite3_exec(db, command, NULL, NULL, NULL);
|
||||
if(rc != SQLITE_OK){
|
||||
break;
|
||||
}
|
||||
}
|
||||
sqlite3_free(attach_command);
|
||||
sqlcipher_free(key, key_sz);
|
||||
|
||||
if(rc == SQLITE_OK){
|
||||
if( !db->autoCommit ){
|
||||
CODEC_TRACE(("cannot migrate from within a transaction"));
|
||||
goto handle_error;
|
||||
}
|
||||
if( db->activeVdbeCnt>1 ){
|
||||
CODEC_TRACE(("cannot migrate - SQL statements in progress"));
|
||||
goto handle_error;
|
||||
}
|
||||
|
||||
/* Save the current value of the database flags so that it can be
|
||||
** restored before returning. Then set the writable-schema flag, and
|
||||
** disable CHECK and foreign key constraints. */
|
||||
saved_flags = db->flags;
|
||||
saved_nChange = db->nChange;
|
||||
saved_nTotalChange = db->nTotalChange;
|
||||
saved_xTrace = db->xTrace;
|
||||
db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks | SQLITE_PreferBuiltin;
|
||||
db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder);
|
||||
db->xTrace = 0;
|
||||
|
||||
Btree *pDest = db->aDb[0].pBt;
|
||||
pDb = &(db->aDb[db->nDb-1]);
|
||||
Btree *pSrc = pDb->pBt;
|
||||
|
||||
rc = sqlite3_exec(db, "BEGIN;", NULL, NULL, NULL);
|
||||
rc = sqlite3BtreeBeginTrans(pSrc, 2);
|
||||
rc = sqlite3BtreeBeginTrans(pDest, 2);
|
||||
|
||||
assert( 1==sqlite3BtreeIsInTrans(pDest) );
|
||||
assert( 1==sqlite3BtreeIsInTrans(pSrc) );
|
||||
|
||||
|
||||
sqlite3CodecGetKey(db, db->nDb - 1, (void**)&key, &password_sz);
|
||||
sqlite3CodecAttach(db, 0, key, password_sz);
|
||||
|
||||
int i = 0;
|
||||
for(i=0; i<ArraySize(aCopy); i+=2){
|
||||
sqlite3BtreeGetMeta(pSrc, aCopy[i], &meta);
|
||||
rc = sqlite3BtreeUpdateMeta(pDest, aCopy[i], meta+aCopy[i+1]);
|
||||
if( NEVER(rc!=SQLITE_OK) ) goto handle_error;
|
||||
}
|
||||
|
||||
rc = sqlite3BtreeCopyFile(pDest, pSrc);
|
||||
if( rc!=SQLITE_OK ) goto handle_error;
|
||||
rc = sqlite3BtreeCommit(pDest);
|
||||
|
||||
db->flags = saved_flags;
|
||||
db->nChange = saved_nChange;
|
||||
db->nTotalChange = saved_nTotalChange;
|
||||
db->xTrace = saved_xTrace;
|
||||
db->autoCommit = 1;
|
||||
if( pDb ){
|
||||
sqlite3BtreeClose(pDb->pBt);
|
||||
pDb->pBt = 0;
|
||||
pDb->pSchema = 0;
|
||||
}
|
||||
sqlite3ResetAllSchemasOfConnection(db);
|
||||
remove(migrated_db_filename);
|
||||
sqlite3_free(migrated_db_filename);
|
||||
} else {
|
||||
CODEC_TRACE(("*** migration failure** \n\n"));
|
||||
}
|
||||
|
||||
}
|
||||
goto exit;
|
||||
|
||||
handle_error:
|
||||
CODEC_TRACE(("An error occurred attempting to migrate the database\n"));
|
||||
rc = SQLITE_ERROR;
|
||||
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sqlcipher_check_connection(char *filename, char *key, int key_sz, char *sql) {
|
||||
int rc;
|
||||
sqlite3 *db;
|
||||
char *errMsg;
|
||||
sqlite3_stmt *statement;
|
||||
char *query_sqlite_master = "SELECT count(*) FROM sqlite_master;";
|
||||
|
||||
rc = sqlite3_open(filename, &db);
|
||||
if(rc != SQLITE_OK){
|
||||
goto cleanup;
|
||||
}
|
||||
rc = sqlite3_key(db, key, key_sz);
|
||||
if(rc != SQLITE_OK){
|
||||
goto cleanup;
|
||||
}
|
||||
rc = sqlite3_exec(db, sql, NULL, NULL, NULL);
|
||||
if(rc != SQLITE_OK){
|
||||
goto cleanup;
|
||||
}
|
||||
rc = sqlite3_prepare(db, query_sqlite_master, -1, &statement, NULL);
|
||||
if(rc != SQLITE_OK){
|
||||
goto cleanup;
|
||||
}
|
||||
if(sqlite3_step(statement) == SQLITE_ROW){
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
goto cleanup;
|
||||
|
||||
cleanup:
|
||||
if(statement){
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
if(db){
|
||||
sqlite3_close(db);
|
||||
}
|
||||
|
||||
exit:
|
||||
return rc;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
/* END SQLCIPHER */
|
||||
|
|
175
test/crypto.test
175
test/crypto.test
|
@ -4,8 +4,6 @@
|
|||
# http://zetetic.net
|
||||
#
|
||||
# Copyright (c) 2009, ZETETIC LLC
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
|
@ -407,9 +405,32 @@ do_test rekey-delete-and-query-wal-3 {
|
|||
db close
|
||||
file delete -force test.db
|
||||
|
||||
# attach an encrypted database
|
||||
# without specifying key, verify it fails
|
||||
# even if the source passwords are the same
|
||||
# because the kdf salts are different
|
||||
setup test.db "'testkey'"
|
||||
do_test attach-database-with-default-key {
|
||||
sqlite_orig db2 test2.db
|
||||
|
||||
execsql {
|
||||
PRAGMA key = 'testkey';
|
||||
CREATE TABLE t2(a,b);
|
||||
INSERT INTO t2 VALUES ('test1', 'test2');
|
||||
} db2
|
||||
|
||||
catchsql {
|
||||
ATTACH 'test.db' AS db;
|
||||
} db2
|
||||
|
||||
} {1 {file is encrypted or is not a database}}
|
||||
db2 close
|
||||
file delete -force test.db
|
||||
file delete -force test2.db
|
||||
|
||||
# attach an encrypted database
|
||||
# where both database have the same
|
||||
# key
|
||||
# key explicitly
|
||||
setup test.db "'testkey'"
|
||||
do_test attach-database-with-same-key {
|
||||
sqlite_orig db2 test2.db
|
||||
|
@ -422,7 +443,7 @@ do_test attach-database-with-same-key {
|
|||
|
||||
execsql {
|
||||
SELECT count(*) FROM t2;
|
||||
ATTACH 'test.db' AS db;
|
||||
ATTACH 'test.db' AS db KEY 'testkey';
|
||||
SELECT count(*) FROM db.t1;
|
||||
} db2
|
||||
|
||||
|
@ -607,7 +628,7 @@ file delete -force test.db
|
|||
|
||||
# create an unencrypted database, attach a new encrypted volume
|
||||
# copy data between, verify the encypted database is good afterwards
|
||||
do_test unencryped-attach {
|
||||
do_test unencrypted-attach {
|
||||
sqlite_orig db test.db
|
||||
|
||||
execsql {
|
||||
|
@ -641,7 +662,7 @@ file delete -force test2.db
|
|||
# create an unencrypted database, attach a new encrypted volume
|
||||
# using a raw key copy data between, verify the encypted
|
||||
# database is good afterwards
|
||||
do_test unencryped-attach-raw-key {
|
||||
do_test unencrypted-attach-raw-key {
|
||||
sqlite_orig db test.db
|
||||
|
||||
execsql {
|
||||
|
@ -672,9 +693,45 @@ db2 close
|
|||
file delete -force test.db
|
||||
file delete -force test2.db
|
||||
|
||||
# create an encrypted database, attach an default-key encrypted volume
|
||||
# copy data between, verify the second database
|
||||
do_test encrypted-attach-default-key {
|
||||
sqlite_orig db test.db
|
||||
|
||||
execsql {
|
||||
PRAGMA key='testkey';
|
||||
CREATE TABLE t1(a,b);
|
||||
BEGIN;
|
||||
}
|
||||
|
||||
for {set i 1} {$i<=1000} {incr i} {
|
||||
set r [expr {int(rand()*500000)}]
|
||||
execsql "INSERT INTO t1 VALUES($i,$r);"
|
||||
}
|
||||
|
||||
execsql {
|
||||
COMMIT;
|
||||
ATTACH DATABASE 'test2.db' AS test;
|
||||
CREATE TABLE test.t1(a,b);
|
||||
INSERT INTO test.t1 SELECT * FROM t1;
|
||||
DETACH DATABASE test;
|
||||
}
|
||||
|
||||
sqlite_orig db2 test2.db
|
||||
|
||||
execsql {
|
||||
PRAGMA key='testkey';
|
||||
SELECT count(*) FROM t1;
|
||||
} db2
|
||||
} {1000}
|
||||
db close
|
||||
db2 close
|
||||
file delete -force test.db
|
||||
file delete -force test2.db
|
||||
|
||||
# create an encrypted database, attach an unencrypted volume
|
||||
# copy data between, verify the unencypted database is good afterwards
|
||||
do_test encryped-attach-unencrypted {
|
||||
do_test encrypted-attach-unencrypted {
|
||||
sqlite_orig db test.db
|
||||
|
||||
execsql {
|
||||
|
@ -683,7 +740,7 @@ do_test encryped-attach-unencrypted {
|
|||
|
||||
sqlite_orig db2 test2.db
|
||||
execsql {
|
||||
PRAGMA key='testkey';
|
||||
PRAGMA key = 'testkey';
|
||||
CREATE TABLE t1(a,b);
|
||||
BEGIN;
|
||||
} db2
|
||||
|
@ -711,7 +768,7 @@ file delete -force test2.db
|
|||
|
||||
# create an unencrypted database, attach an unencrypted volume
|
||||
# copy data between, verify the unencypted database is good afterwards
|
||||
do_test unencryped-attach-unencrypted {
|
||||
do_test unencrypted-attach-unencrypted {
|
||||
sqlite_orig db test.db
|
||||
|
||||
execsql {
|
||||
|
@ -879,6 +936,7 @@ do_test open-1.1.8-database {
|
|||
execsql {
|
||||
PRAGMA key = 'testkey';
|
||||
PRAGMA cipher_use_hmac = OFF;
|
||||
PRAGMA kdf_iter = 4000;
|
||||
SELECT count(*) FROM t1;
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
|
@ -893,6 +951,7 @@ do_test attach-and-copy-1.1.8 {
|
|||
execsql {
|
||||
PRAGMA key = 'testkey';
|
||||
PRAGMA cipher_use_hmac = OFF;
|
||||
PRAGMA kdf_iter = 4000;
|
||||
ATTACH DATABASE 'test.db' AS db2 KEY 'testkey-hmac';
|
||||
CREATE TABLE db2.t1(a,b);
|
||||
INSERT INTO db2.t1 SELECT * FROM main.t1;
|
||||
|
@ -1350,20 +1409,22 @@ do_test cipher-options-before-keys {
|
|||
db close
|
||||
file delete -force test.db
|
||||
|
||||
# open a 1.1.8 database (no HMAC), then
|
||||
# open a 1.1.8 database (no HMAC, 4K iter), then
|
||||
# try to open another 1.1.8 database. The
|
||||
# attached database should have the same hmac
|
||||
# setting as the original
|
||||
do_test default-use-hmac-attach {
|
||||
do_test default-hmac-kdf-attach {
|
||||
file copy -force sqlcipher-1.1.8-testkey.db test.db
|
||||
sqlite_orig db test.db
|
||||
execsql {
|
||||
PRAGMA cipher_default_use_hmac = OFF;
|
||||
PRAGMA cipher_default_kdf_iter = 4000;
|
||||
PRAGMA key = 'testkey';
|
||||
SELECT count(*) FROM t1;
|
||||
ATTACH 'sqlcipher-1.1.8-testkey.db' AS db2;
|
||||
ATTACH 'sqlcipher-1.1.8-testkey.db' AS db2 KEY 'testkey';
|
||||
SELECT count(*) from db2.t1;
|
||||
PRAGMA cipher_default_use_hmac = ON;
|
||||
PRAGMA cipher_default_kdf_iter = 64000;
|
||||
}
|
||||
} {4 4}
|
||||
db close
|
||||
|
@ -1378,18 +1439,18 @@ do_test attach-1.1.8-database-from-2.0-fails {
|
|||
catchsql {
|
||||
PRAGMA key = 'testkey';
|
||||
CREATE table t1(a,b);
|
||||
ATTACH 'sqlcipher-1.1.8-testkey.db' AS db2;
|
||||
ATTACH 'sqlcipher-1.1.8-testkey.db' AS db2 KEY 'testkey';
|
||||
}
|
||||
} {1 {file is encrypted or is not a database}}
|
||||
db close
|
||||
file delete -force test.db
|
||||
|
||||
# open a 2.0 database (with HMAC), then
|
||||
# open a 2.0 database (with HMAC, 4k iter), then
|
||||
# set the default hmac setting to OFF.
|
||||
# try to a 1.1.8 database. this should
|
||||
# succeed now that hmac is off by default
|
||||
# before the attach
|
||||
do_test change-default-use-hmac-attach {
|
||||
do_test change-default-hmac-kdf-attach {
|
||||
sqlite_orig db test.db
|
||||
execsql {
|
||||
PRAGMA key = 'testkey';
|
||||
|
@ -1402,9 +1463,11 @@ do_test change-default-use-hmac-attach {
|
|||
PRAGMA key = 'testkey';
|
||||
SELECT count(*) FROM t1;
|
||||
PRAGMA cipher_default_use_hmac = OFF;
|
||||
ATTACH 'sqlcipher-1.1.8-testkey.db' AS db2;
|
||||
PRAGMA cipher_default_kdf_iter = 4000;
|
||||
ATTACH 'sqlcipher-1.1.8-testkey.db' AS db2 KEY 'testkey';
|
||||
SELECT count(*) from db2.t1;
|
||||
PRAGMA cipher_default_use_hmac = ON;
|
||||
PRAGMA cipher_default_kdf_iter = 64000;
|
||||
}
|
||||
} {1 4}
|
||||
db close
|
||||
|
@ -1580,12 +1643,25 @@ do_test multipage-schema-autovacuum-shortread-wal {
|
|||
db close
|
||||
file delete -force test.db
|
||||
|
||||
# open a 2.3 database with little endian hmac page numbers (default)
|
||||
# verify it can be opened
|
||||
do_test open-2.3-le-database {
|
||||
sqlite_orig db sqlcipher-2.3-testkey.db
|
||||
execsql {
|
||||
PRAGMA key = 'testkey';
|
||||
SELECT count(*) FROM t1;
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {4 1 1 one one 1 2 one two}
|
||||
db close
|
||||
|
||||
# open a 2.0 database with little endian hmac page numbers (default)
|
||||
# verify it can be opened
|
||||
do_test open-2.0-le-database {
|
||||
sqlite_orig db sqlcipher-2.0-le-testkey.db
|
||||
execsql {
|
||||
PRAGMA key = 'testkey';
|
||||
PRAGMA kdf_iter = 4000;
|
||||
SELECT count(*) FROM t1;
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
|
@ -1599,6 +1675,7 @@ do_test open-2.0-be-database {
|
|||
execsql {
|
||||
PRAGMA key = 'testkey';
|
||||
PRAGMA cipher_hmac_pgno = be;
|
||||
PRAGMA kdf_iter = 4000;
|
||||
SELECT count(*) FROM t1;
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
|
@ -1615,6 +1692,7 @@ do_test be-to-le-migration {
|
|||
execsql {
|
||||
PRAGMA key = 'testkey';
|
||||
PRAGMA cipher_hmac_pgno = be;
|
||||
PRAGMA kdf_iter = 4000;
|
||||
ATTACH DATABASE 'test.db' AS db2 KEY 'testkey';
|
||||
CREATE TABLE db2.t1(a,b);
|
||||
INSERT INTO db2.t1 SELECT * FROM main.t1;
|
||||
|
@ -1684,6 +1762,31 @@ do_test verify-pragma-cipher-default-use-hmac-off {
|
|||
db close
|
||||
file delete -force test.db
|
||||
|
||||
# verify the pragma default_cipher_kdf_iter
|
||||
# is set to 64000 by default
|
||||
do_test verify-pragma-cipher-default-kdf-iter-default {
|
||||
sqlite_orig db test.db
|
||||
execsql {
|
||||
PRAGMA cipher_default_kdf_iter;
|
||||
}
|
||||
} {64000}
|
||||
db close
|
||||
file delete -force test.db
|
||||
|
||||
|
||||
# verify the pragma default_cipher_kdf_ter
|
||||
# reports changes
|
||||
do_test verify-pragma-cipher-default-use-hmac-off {
|
||||
sqlite_orig db test.db
|
||||
execsql {
|
||||
PRAGMA cipher_default_kdf_iter = 1000;
|
||||
PRAGMA cipher_default_kdf_iter;
|
||||
PRAGMA cipher_default_kdf_iter = 64000;
|
||||
}
|
||||
} {1000}
|
||||
db close
|
||||
file delete -force test.db
|
||||
|
||||
# verify the pragma kdf_iter
|
||||
# reports the default value
|
||||
do_test verify-pragma-kdf-iter-reports-default {
|
||||
|
@ -1692,7 +1795,7 @@ do_test verify-pragma-kdf-iter-reports-default {
|
|||
PRAGMA key = 'test';
|
||||
PRAGMA kdf_iter;
|
||||
}
|
||||
} {4000}
|
||||
} {64000}
|
||||
db close
|
||||
file delete -force test.db
|
||||
|
||||
|
@ -1843,6 +1946,7 @@ do_test open-2.0-beta-database {
|
|||
sqlite_orig db sqlcipher-2.0-beta-testkey.db
|
||||
execsql {
|
||||
PRAGMA key = 'testkey';
|
||||
PRAGMA kdf_iter = 4000;
|
||||
PRAGMA fast_kdf_iter = 4000;
|
||||
PRAGMA cipher_hmac_salt_mask = "x'00'";
|
||||
SELECT count(*) FROM t1;
|
||||
|
@ -1861,6 +1965,7 @@ do_test 2.0-beta-to-2.0-migration {
|
|||
execsql {
|
||||
PRAGMA key = 'testkey';
|
||||
PRAGMA cipher_hmac_salt_mask = "x'00'";
|
||||
PRAGMA kdf_iter = 4000;
|
||||
PRAGMA fast_kdf_iter = 4000;
|
||||
SELECT count(*) FROM sqlite_master;
|
||||
|
||||
|
@ -1902,4 +2007,40 @@ if_built_with_commoncrypto verify-default-cipher {
|
|||
db close
|
||||
file delete -force test.db
|
||||
|
||||
do_test migrate-1.1.8-database-to-3x-format {
|
||||
file copy -force sqlcipher-1.1.8-testkey.db test.db
|
||||
sqlite_orig db test.db
|
||||
execsql {
|
||||
PRAGMA key = 'testkey';
|
||||
PRAGMA cipher_migrate;
|
||||
}
|
||||
db close
|
||||
|
||||
sqlite_orig db test.db
|
||||
execsql {
|
||||
PRAGMA key = 'testkey';
|
||||
SELECT count(*) FROM sqlite_master;
|
||||
}
|
||||
} {1}
|
||||
db close
|
||||
file delete -force test.db
|
||||
|
||||
do_test migrate-2-0-le-database-to-3x-format {
|
||||
file copy -force sqlcipher-2.0-le-testkey.db test.db
|
||||
sqlite_orig db test.db
|
||||
execsql {
|
||||
PRAGMA key = 'testkey';
|
||||
PRAGMA cipher_migrate;
|
||||
}
|
||||
db close
|
||||
|
||||
sqlite_orig db test.db
|
||||
execsql {
|
||||
PRAGMA key = 'testkey';
|
||||
SELECT count(*) FROM sqlite_master;
|
||||
}
|
||||
} {1}
|
||||
db close
|
||||
file delete -force test.db
|
||||
|
||||
finish_test
|
||||
|
|
Loading…
Reference in New Issue