summaryrefslogtreecommitdiff
path: root/src/crypto_impl.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/crypto_impl.c')
-rw-r--r--src/crypto_impl.c456
1 files changed, 418 insertions, 38 deletions
diff --git a/src/crypto_impl.c b/src/crypto_impl.c
index 1e7fa99..9a77e0f 100644
--- a/src/crypto_impl.c
+++ b/src/crypto_impl.c
@@ -47,6 +47,7 @@
to keep track of read / write state separately. The following
struct and associated functions are defined here */
typedef struct {
+ int store_pass;
int derive_key;
int kdf_iter;
int fast_kdf_iter;
@@ -56,16 +57,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;
+ unsigned 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;
@@ -79,6 +83,8 @@ struct codec_ctx {
Btree *pBt;
cipher_ctx *read_ctx;
cipher_ctx *write_ctx;
+ unsigned int skip_read_hmac;
+ unsigned int need_kdf_salt;
};
int sqlcipher_register_provider(sqlcipher_provider *p) {
@@ -215,7 +221,9 @@ void sqlcipher_free(void *ptr, int sz) {
#if defined(__unix__) || defined(__APPLE__)
munlock(ptr, sz);
#elif defined(_WIN32)
- VirtualUnlock(ptr, sz);
+#if !(defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP || WINAPI_FAMILY == WINAPI_FAMILY_APP))
+VirtualUnlock(ptr, sz);
+#endif
#endif
#endif
}
@@ -236,8 +244,10 @@ void* sqlcipher_malloc(int sz) {
#if defined(__unix__) || defined(__APPLE__)
mlock(ptr, sz);
#elif defined(_WIN32)
+#if !(defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP || WINAPI_FAMILY == WINAPI_FAMILY_APP))
VirtualLock(ptr, sz);
#endif
+#endif
}
#endif
return ptr;
@@ -289,6 +299,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));
}
@@ -299,9 +310,7 @@ static void sqlcipher_cipher_ctx_free(cipher_ctx **iCtx) {
* returns 1 otherwise
*/
static int sqlcipher_cipher_ctx_cmp(cipher_ctx *c1, cipher_ctx *c2) {
- CODEC_TRACE(("sqlcipher_cipher_ctx_cmp: entered c1=%p c2=%p\n", c1, c2));
-
- if(
+ int are_equal = (
c1->iv_sz == c2->iv_sz
&& c1->kdf_iter == c2->kdf_iter
&& c1->fast_kdf_iter == c2->fast_kdf_iter
@@ -315,9 +324,43 @@ static int sqlcipher_cipher_ctx_cmp(cipher_ctx *c1, cipher_ctx *c2) {
|| !sqlcipher_memcmp((const unsigned char*)c1->pass,
(const unsigned char*)c2->pass,
c1->pass_sz)
- )
- ) return 0;
- return 1;
+ ));
+
+ CODEC_TRACE(("sqlcipher_cipher_ctx_cmp: entered \
+ c1=%p c2=%p \
+ c1->iv_sz=%d c2->iv_sz=%d \
+ c1->kdf_iter=%d c2->kdf_iter=%d \
+ c1->fast_kdf_iter=%d c2->fast_kdf_iter=%d \
+ c1->key_sz=%d c2->key_sz=%d \
+ c1->pass_sz=%d c2->pass_sz=%d \
+ c1->flags=%d c2->flags=%d \
+ c1->hmac_sz=%d c2->hmac_sz=%d \
+ c1->provider_ctx=%p c2->provider_ctx=%p \
+ c1->pass=%p c2->pass=%p \
+ c1->pass=%s c2->pass=%s \
+ provider->ctx_cmp=%d \
+ sqlcipher_memcmp=%d \
+ are_equal=%d \
+ \n",
+ c1, c2,
+ c1->iv_sz, c2->iv_sz,
+ c1->kdf_iter, c2->kdf_iter,
+ c1->fast_kdf_iter, c2->fast_kdf_iter,
+ c1->key_sz, c2->key_sz,
+ c1->pass_sz, c2->pass_sz,
+ c1->flags, c2->flags,
+ c1->hmac_sz, c2->hmac_sz,
+ c1->provider_ctx, c2->provider_ctx,
+ c1->pass, c2->pass,
+ c1->pass, c2->pass,
+ c1->provider->ctx_cmp(c1->provider_ctx, c2->provider_ctx),
+ sqlcipher_memcmp((const unsigned char*)c1->pass,
+ (const unsigned char*)c2->pass,
+ c1->pass_sz),
+ are_equal
+ ));
+
+ return !are_equal; /* return 0 if they are the same, 1 otherwise */
}
/**
@@ -336,8 +379,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 +394,79 @@ 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] = '\'';
+ cipher_bin2hex(key, key_sz, ctx->keyspec + 2);
+ cipher_bin2hex(salt, salt_sz, ctx->keyspec + (key_sz * 2) + 2);
+ ctx->keyspec[ctx->keyspec_sz - 1] = '\'';
return SQLITE_OK;
}
+static int sqlcipher_codec_get_store_pass(codec_ctx *ctx) {
+ return ctx->read_ctx->store_pass;
+}
+
+static void sqlcipher_codec_set_store_pass(codec_ctx *ctx, int value) {
+ ctx->read_ctx->store_pass = value;
+}
+
+static void sqlcipher_codec_get_pass(codec_ctx *ctx, void **zKey, int *nKey) {
+ *zKey = ctx->read_ctx->pass;
+ *nKey = ctx->read_ctx->pass_sz;
+}
/**
- * 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 +507,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 +638,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) {
@@ -596,12 +697,11 @@ int sqlcipher_codec_ctx_init(codec_ctx **iCtx, Db *pDb, Pager *pPager, sqlite3_f
if((rc = sqlcipher_cipher_ctx_init(&ctx->write_ctx)) != SQLITE_OK) return rc;
if(fd == NULL || sqlite3OsRead(fd, ctx->kdf_salt, FILE_HEADER_SZ, 0) != SQLITE_OK) {
- /* if unable to read the bytes, generate random salt */
- if(ctx->read_ctx->provider->random(ctx->read_ctx->provider_ctx, ctx->kdf_salt, FILE_HEADER_SZ) != SQLITE_OK) return SQLITE_ERROR;
+ ctx->need_kdf_salt = 1;
}
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;
@@ -707,7 +807,7 @@ int sqlcipher_page_cipher(codec_ctx *ctx, int for_ctx, Pgno pgno, int mode, int
memcpy(iv_out, iv_in, c_ctx->iv_sz); /* copy the iv from the input to output buffer */
}
- if((c_ctx->flags & CIPHER_FLAG_HMAC) && (mode == CIPHER_DECRYPT)) {
+ if((c_ctx->flags & CIPHER_FLAG_HMAC) && (mode == CIPHER_DECRYPT) && !ctx->skip_read_hmac) {
if(sqlcipher_page_hmac(c_ctx, pgno, in, size + c_ctx->iv_sz, hmac_out) != SQLITE_OK) {
sqlcipher_memset(out, 0, page_sz);
CODEC_TRACE(("codec_cipher: hmac operations failed for pgno=%d\n", pgno));
@@ -750,7 +850,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,27 +862,40 @@ 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,
ctx->hmac_kdf_salt, c_ctx->fast_kdf_iter, c_ctx->key_sz));
-
+
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(ctx->need_kdf_salt) {
+ if(ctx->read_ctx->provider->random(ctx->read_ctx->provider_ctx, ctx->kdf_salt, FILE_HEADER_SZ) != SQLITE_OK) return SQLITE_ERROR;
+ ctx->need_kdf_salt = 0;
+ }
+ if (c_ctx->pass_sz == ((c_ctx->key_sz * 2) + 3) && sqlite3StrNICmp((const char *)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"));
+ const unsigned 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, n, c_ctx->key);
+ } else if (c_ctx->pass_sz == (((c_ctx->key_sz + ctx->kdf_salt_sz) * 2) + 3) && sqlite3StrNICmp((const char *)c_ctx->pass ,"x'", 2) == 0) {
+ const unsigned 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));
- c_ctx->provider->kdf(c_ctx->provider_ctx, (const char*) c_ctx->pass, c_ctx->pass_sz,
+ 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, 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,11 +912,11 @@ 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));
- c_ctx->provider->kdf(c_ctx->provider_ctx, (const char*)c_ctx->key, c_ctx->key_sz,
+ c_ctx->provider->kdf(c_ctx->provider_ctx, c_ctx->key, c_ctx->key_sz,
ctx->hmac_kdf_salt, ctx->kdf_salt_sz, c_ctx->fast_kdf_iter,
c_ctx->key_sz, c_ctx->hmac_key);
}
@@ -818,12 +935,19 @@ 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 */
+ if(ctx->read_ctx->store_pass != 1) {
+ 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 +963,261 @@ const char* sqlcipher_codec_get_cipher_provider(codec_ctx *ctx) {
return ctx->read_ctx->provider->get_provider_name(ctx->read_ctx);
}
+
+static int sqlcipher_check_connection(const char *filename, char *key, int key_sz, char *sql, int *user_version) {
+ int rc;
+ sqlite3 *db = NULL;
+ sqlite3_stmt *statement = NULL;
+ char *query_user_version = "PRAGMA user_version;";
+
+ 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_user_version, -1, &statement, NULL);
+ if(rc != SQLITE_OK){
+ goto cleanup;
+ }
+ rc = sqlite3_step(statement);
+ if(rc == SQLITE_ROW){
+ *user_version = sqlite3_column_int(statement, 0);
+ rc = SQLITE_OK;
+ }
+
+cleanup:
+ if(statement){
+ sqlite3_finalize(statement);
+ }
+ if(db){
+ sqlite3_close(db);
+ }
+ return rc;
+}
+
+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 *pragma_hmac_off = "PRAGMA cipher_use_hmac = OFF;";
+ char *pragma_4k_kdf_iter = "PRAGMA kdf_iter = 4000;";
+ char *pragma_1x_and_4k;
+ char *set_user_version;
+ char *key;
+ int key_sz;
+ int user_version = 0;
+ int upgrade_1x_format = 0;
+ int upgrade_4k_format = 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){
+ const char* commands[5];
+ char *attach_command = sqlite3_mprintf("ATTACH DATABASE '%s-migrated' as migrate KEY '%q';",
+ db_filename, key);
+
+ int rc = sqlcipher_check_connection(db_filename, key, ctx->read_ctx->pass_sz, "", &user_version);
+ 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, ctx->read_ctx->pass_sz, pragma_4k_kdf_iter, &user_version);
+ if(rc == SQLITE_OK) {
+ CODEC_TRACE(("Version 2 format found\n"));
+ upgrade_4k_format = 1;
+ }
+
+ // Version 1 - check both no hmac and 4k together
+ pragma_1x_and_4k = sqlite3_mprintf("%s%s", pragma_hmac_off,
+ pragma_4k_kdf_iter);
+ rc = sqlcipher_check_connection(db_filename, key, ctx->read_ctx->pass_sz, pragma_1x_and_4k, &user_version);
+ 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;
+ }
+
+ set_user_version = sqlite3_mprintf("PRAGMA migrate.user_version = %d;", user_version);
+ commands[0] = upgrade_4k_format == 1 ? pragma_4k_kdf_iter : "";
+ commands[1] = upgrade_1x_format == 1 ? pragma_hmac_off : "";
+ commands[2] = attach_command;
+ commands[3] = "SELECT sqlcipher_export('migrate');";
+ commands[4] = set_user_version;
+
+ 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);
+ sqlite3_free(set_user_version);
+ sqlcipher_free(key, key_sz);
+
+ if(rc == SQLITE_OK){
+ Btree *pDest;
+ Btree *pSrc;
+ int i = 0;
+
+ if( !db->autoCommit ){
+ CODEC_TRACE(("cannot migrate from within a transaction"));
+ goto handle_error;
+ }
+ if( db->nVdbeActive>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;
+
+ pDest = db->aDb[0].pBt;
+ pDb = &(db->aDb[db->nDb-1]);
+ 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);
+ sqlite3pager_get_codec(pDest->pBt->pPager, (void**)&ctx);
+
+ ctx->skip_read_hmac = 1;
+ 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);
+ ctx->skip_read_hmac = 0;
+ 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_codec_add_random(codec_ctx *ctx, const char *zRight, int random_sz){
+ const char *suffix = &zRight[random_sz-1];
+ int n = random_sz - 3; /* adjust for leading x' and tailing ' */
+ if (n > 0 &&
+ sqlite3StrNICmp((const char *)zRight ,"x'", 2) == 0 &&
+ sqlite3StrNICmp(suffix, "'", 1) == 0 &&
+ n % 2 == 0) {
+ int rc = 0;
+ int buffer_sz = n / 2;
+ unsigned char *random;
+ const unsigned char *z = (const unsigned char *)zRight + 2; /* adjust lead offset of x' */
+ CODEC_TRACE(("sqlcipher_codec_add_random: using raw random blob from hex\n"));
+ random = sqlcipher_malloc(buffer_sz);
+ memset(random, 0, buffer_sz);
+ cipher_hex2bin(z, n, random);
+ rc = ctx->read_ctx->provider->add_random(ctx->read_ctx->provider_ctx, random, buffer_sz);
+ sqlcipher_free(random, buffer_sz);
+ return rc;
+ }
+ return SQLITE_ERROR;
+}
+
+int sqlcipher_cipher_profile(sqlite3 *db, const char *destination){
+ FILE *f;
+ if( strcmp(destination,"stdout")==0 ){
+ f = stdout;
+ }else if( strcmp(destination, "stderr")==0 ){
+ f = stderr;
+ }else if( strcmp(destination, "off")==0 ){
+ f = 0;
+ }else{
+ f = fopen(destination, "wb");
+ if( f==0 ){
+ return SQLITE_ERROR;
+ }
+ }
+ sqlite3_profile(db, sqlcipher_profile_callback, f);
+ return SQLITE_OK;
+}
+
+static void sqlcipher_profile_callback(void *file, const char *sql, sqlite3_uint64 run_time){
+ FILE *f = (FILE*)file;
+ double elapsed = run_time/1000000.0;
+ if( f ) fprintf(f, "Elapsed time:%.3f ms - %s\n", elapsed, sql);
+}
+
+
#endif
/* END SQLCIPHER */