From 569c6676a6ddb0ff73821d7693b5e18ddef809b9 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Thu, 16 Oct 2014 22:51:35 -0400 Subject: Imported Upstream version 3.2.0 --- src/crypto_impl.c | 456 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 418 insertions(+), 38 deletions(-) (limited to 'src/crypto_impl.c') 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,7 +244,9 @@ 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 @@ -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; iskip_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 */ -- cgit v1.2.3