static int codec_set_pass_key(sqlite3* db, int nDb, const void *zKey, int nKey, int for_ctx) { struct Db *pDb = &db->aDb[nDb]; CODEC_TRACE(("codec_set_pass_key: entered db=%p nDb=%d zKey=%s nKey=%d for_ctx=%d\n", db, nDb, (char *)zKey, nKey, for_ctx)); if(pDb->pBt) { codec_ctx *ctx; sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx); if(ctx) return sqlcipher_codec_ctx_set_pass(ctx, zKey, nKey, for_ctx); } return SQLITE_ERROR; }
/** * * when for_ctx == 0 then it will change for read * when for_ctx == 1 then it will change for write * when for_ctx == 2 then it will change for both */ int codec_set_cipher_name(sqlite3* db, int nDb, const char *cipher_name, int for_ctx) { struct Db *pDb = &db->aDb[nDb]; CODEC_TRACE(("codec_set_cipher_name: entered db=%d nDb=%d cipher_name=%s for_ctx=%d\n", db, nDb, cipher_name, for_ctx)); if(pDb->pBt) { codec_ctx *ctx; sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx); return sqlcipher_codec_ctx_set_cipher(ctx, cipher_name, for_ctx); } return SQLITE_ERROR; }
int codec_set_fast_kdf_iter(sqlite3* db, int nDb, int kdf_iter, int for_ctx) { struct Db *pDb = &db->aDb[nDb]; CODEC_TRACE(("codec_set_kdf_iter: entered db=%d nDb=%d kdf_iter=%d for_ctx=%d\n", db, nDb, kdf_iter, for_ctx)); if(pDb->pBt) { codec_ctx *ctx; sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx); return sqlcipher_codec_ctx_set_fast_kdf_iter(ctx, kdf_iter, for_ctx); } return SQLITE_ERROR; }
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_keyspec(ctx, zKey, nKey); } else { *zKey = NULL; *nKey = 0; } } }
int codec_set_page_size(sqlite3* db, int nDb, int size) { struct Db *pDb = &db->aDb[nDb]; CODEC_TRACE(("codec_set_page_size: entered db=%d nDb=%d size=%d\n", db, nDb, size)); if(pDb->pBt) { int rc; codec_ctx *ctx; sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx); rc = sqlcipher_codec_ctx_set_pagesize(ctx, size); if(rc != SQLITE_OK) return rc; return codec_set_btree_to_codec_pagesize(db, pDb, ctx); } return SQLITE_ERROR; }
int codec_set_pass_key(sqlite3* db, int nDb, const void *zKey, int nKey, int for_ctx) { struct Db *pDb = &db->aDb[nDb]; CODEC_TRACE(("codec_set_pass_key: entered db=%d nDb=%d cipher_name=%s nKey=%d for_ctx=%d\n", db, nDb, zKey, nKey, for_ctx)); if(pDb->pBt) { codec_ctx *ctx; cipher_ctx *c_ctx; sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx); c_ctx = for_ctx ? ctx->write_ctx : ctx->read_ctx; cipher_ctx_set_pass(c_ctx, zKey, nKey); c_ctx->derive_key = 1; if(for_ctx == 2) cipher_ctx_copy( for_ctx ? ctx->read_ctx : ctx->write_ctx, c_ctx); return SQLITE_OK; } return SQLITE_ERROR; }
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(sqlcipher_codec_get_store_pass(ctx) == 1) { sqlcipher_codec_get_pass(ctx, zKey, nKey); } else { sqlcipher_codec_get_keyspec(ctx, zKey, nKey); } } else { *zKey = NULL; *nKey = 0; } } }
int codec_set_kdf_iter(sqlite3* db, int nDb, int kdf_iter, int for_ctx) { struct Db *pDb = &db->aDb[nDb]; CODEC_TRACE(("codec_set_kdf_iter: entered db=%d nDb=%d kdf_iter=%d for_ctx=%d\n", db, nDb, kdf_iter, for_ctx)); if(pDb->pBt) { codec_ctx *ctx; cipher_ctx *c_ctx; sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx); c_ctx = for_ctx ? ctx->write_ctx : ctx->read_ctx; c_ctx->kdf_iter = kdf_iter; c_ctx->derive_key = 1; if(for_ctx == 2) cipher_ctx_copy( for_ctx ? ctx->read_ctx : ctx->write_ctx, c_ctx); return SQLITE_OK; } return SQLITE_ERROR; }
void sqlite3CodecGetKey(sqlite3* db, int nDb, void **zKey, int *nKey) { codec_ctx *ctx; struct Db *pDb = &db->aDb[nDb]; if( pDb->pBt ) { sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx); /* if the codec has an attached codec_context user the raw key data */ if(ctx) { *zKey = ctx->pass; *nKey = ctx->pass_sz; } else { *zKey = 0; *nKey = 0; } } }
int codec_set_use_hmac(sqlite3* db, int nDb, int use) { struct Db *pDb = &db->aDb[nDb]; CODEC_TRACE(("codec_set_use_hmac: entered db=%d nDb=%d use=%d\n", db, nDb, use)); if(pDb->pBt) { int rc; codec_ctx *ctx; sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx); rc = sqlcipher_codec_ctx_set_use_hmac(ctx, use); if(rc != SQLITE_OK) return rc; /* since the use of hmac has changed, the page size may also change */ /* Note: before forcing the page size we need to force pageSizeFixed to 0, else sqliteBtreeSetPageSize will block the change */ return codec_set_btree_to_codec_pagesize(db, pDb, ctx); } return SQLITE_ERROR; }
/** * * when for_ctx == 0 then it will change for read * when for_ctx == 1 then it will change for write * when for_ctx == 2 then it will change for both */ int codec_set_cipher_name(sqlite3* db, int nDb, const char *cipher_name, int for_ctx) { struct Db *pDb = &db->aDb[nDb]; CODEC_TRACE(("codec_set_cipher_name: entered db=%d nDb=%d cipher_name=%s for_ctx=%d\n", db, nDb, cipher_name, for_ctx)); if(pDb->pBt) { codec_ctx *ctx; cipher_ctx *c_ctx; sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx); c_ctx = for_ctx ? ctx->write_ctx : ctx->read_ctx; c_ctx->evp_cipher = (EVP_CIPHER *) EVP_get_cipherbyname(cipher_name); c_ctx->key_sz = EVP_CIPHER_key_length(c_ctx->evp_cipher); c_ctx->iv_sz = EVP_CIPHER_iv_length(c_ctx->evp_cipher); c_ctx->derive_key = 1; if(for_ctx == 2) cipher_ctx_copy( for_ctx ? ctx->read_ctx : ctx->write_ctx, c_ctx); return SQLITE_OK; } return SQLITE_ERROR; }
int sqlcipher_codec_pragma(sqlite3* db, int iDb, Parse *pParse, const char *zLeft, const char *zRight) { struct Db *pDb = &db->aDb[iDb]; codec_ctx *ctx = NULL; int rc; if(pDb->pBt) { sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx); } CODEC_TRACE(("sqlcipher_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_store_pass")==0 && zRight ) { sqlcipher_codec_set_store_pass(ctx, sqlite3GetBoolean(zRight, 1)); } else if( sqlite3StrICmp(zLeft, "cipher_store_pass")==0 && !zRight ) { char *store_pass_value = sqlite3_mprintf("%d", sqlcipher_codec_get_store_pass(ctx)); codec_vdbe_return_static_string(pParse, "cipher_store_pass", store_pass_value); sqlite3_free(store_pass_value); } if( sqlite3StrICmp(zLeft, "cipher_profile")== 0 && zRight ){ char *profile_status = sqlite3_mprintf("%d", sqlcipher_cipher_profile(db, zRight)); codec_vdbe_return_static_string(pParse, "cipher_profile", profile_status); sqlite3_free(profile_status); } else if( sqlite3StrICmp(zLeft, "cipher_add_random")==0 && zRight ){ if(ctx) { char *add_random_status = sqlite3_mprintf("%d", sqlcipher_codec_add_random(ctx, zRight, sqlite3Strlen30(zRight))); codec_vdbe_return_static_string(pParse, "cipher_add_random", add_random_status); sqlite3_free(add_random_status); } } else 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, "cipher_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)); } } else if( sqlite3StrICmp(zLeft, "cipher_version")==0 && !zRight ){ codec_vdbe_return_static_string(pParse, "cipher_version", codec_get_cipher_version()); }else if( sqlite3StrICmp(zLeft, "cipher")==0 ){ if(ctx) { if( zRight ) { sqlcipher_codec_ctx_set_cipher(ctx, zRight, 2); // change cipher for both }else { codec_vdbe_return_static_string(pParse, "cipher", sqlcipher_codec_ctx_get_cipher(ctx, 2)); } } }else 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 ) { sqlcipher_codec_ctx_set_kdf_iter(ctx, atoi(zRight), 2); // change of RW PBKDF2 iteration } else { char *kdf_iter = sqlite3_mprintf("%d", sqlcipher_codec_ctx_get_kdf_iter(ctx, 2)); codec_vdbe_return_static_string(pParse, "kdf_iter", kdf_iter); sqlite3_free(kdf_iter); } } }else if( sqlite3StrICmp(zLeft, "fast_kdf_iter")==0){ if(ctx) { if( zRight ) { sqlcipher_codec_ctx_set_fast_kdf_iter(ctx, atoi(zRight), 2); // change of RW PBKDF2 iteration } else { char *fast_kdf_iter = sqlite3_mprintf("%d", sqlcipher_codec_ctx_get_fast_kdf_iter(ctx, 2)); codec_vdbe_return_static_string(pParse, "fast_kdf_iter", fast_kdf_iter); sqlite3_free(fast_kdf_iter); } } }else if( sqlite3StrICmp(zLeft, "rekey_kdf_iter")==0 && zRight ){ if(ctx) sqlcipher_codec_ctx_set_kdf_iter(ctx, atoi(zRight), 1); // write iterations only }else if( sqlite3StrICmp(zLeft,"cipher_page_size")==0 ){ if(ctx) { if( zRight ) { int size = atoi(zRight); rc = sqlcipher_codec_ctx_set_pagesize(ctx, size); if(rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, rc); rc = codec_set_btree_to_codec_pagesize(db, pDb, ctx); if(rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, rc); } else { char * page_size = sqlite3_mprintf("%d", sqlcipher_codec_ctx_get_pagesize(ctx)); codec_vdbe_return_static_string(pParse, "cipher_page_size", page_size); sqlite3_free(page_size); } } }else if( sqlite3StrICmp(zLeft,"cipher_default_use_hmac")==0 ){ if( zRight ) { sqlcipher_set_default_use_hmac(sqlite3GetBoolean(zRight,1)); } else { char *default_use_hmac = sqlite3_mprintf("%d", sqlcipher_get_default_use_hmac()); codec_vdbe_return_static_string(pParse, "cipher_default_use_hmac", default_use_hmac); sqlite3_free(default_use_hmac); } }else if( sqlite3StrICmp(zLeft,"cipher_use_hmac")==0 ){ if(ctx) { if( zRight ) { rc = sqlcipher_codec_ctx_set_use_hmac(ctx, sqlite3GetBoolean(zRight,1)); if(rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, rc); /* since the use of hmac has changed, the page size may also change */ rc = codec_set_btree_to_codec_pagesize(db, pDb, ctx); if(rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, rc); } else { char *hmac_flag = sqlite3_mprintf("%d", sqlcipher_codec_ctx_get_use_hmac(ctx, 2)); codec_vdbe_return_static_string(pParse, "cipher_use_hmac", hmac_flag); sqlite3_free(hmac_flag); } } }else if( sqlite3StrICmp(zLeft,"cipher_hmac_pgno")==0 ){ if(ctx) { if(zRight) { // clear both pgno endian flags if(sqlite3StrICmp(zRight, "le") == 0) { sqlcipher_codec_ctx_unset_flag(ctx, CIPHER_FLAG_BE_PGNO); sqlcipher_codec_ctx_set_flag(ctx, CIPHER_FLAG_LE_PGNO); } else if(sqlite3StrICmp(zRight, "be") == 0) { sqlcipher_codec_ctx_unset_flag(ctx, CIPHER_FLAG_LE_PGNO); sqlcipher_codec_ctx_set_flag(ctx, CIPHER_FLAG_BE_PGNO); } else if(sqlite3StrICmp(zRight, "native") == 0) { sqlcipher_codec_ctx_unset_flag(ctx, CIPHER_FLAG_LE_PGNO); sqlcipher_codec_ctx_unset_flag(ctx, CIPHER_FLAG_BE_PGNO); } } else { if(sqlcipher_codec_ctx_get_flag(ctx, CIPHER_FLAG_LE_PGNO, 2)) { codec_vdbe_return_static_string(pParse, "cipher_hmac_pgno", "le"); } else if(sqlcipher_codec_ctx_get_flag(ctx, CIPHER_FLAG_BE_PGNO, 2)) { codec_vdbe_return_static_string(pParse, "cipher_hmac_pgno", "be"); } else { codec_vdbe_return_static_string(pParse, "cipher_hmac_pgno", "native"); } } } }else if( sqlite3StrICmp(zLeft,"cipher_hmac_salt_mask")==0 ){ if(ctx) { if(zRight) { if (sqlite3StrNICmp(zRight ,"x'", 2) == 0 && sqlite3Strlen30(zRight) == 5) { unsigned char mask = 0; const unsigned char *hex = (const unsigned char *)zRight+2; cipher_hex2bin(hex,2,&mask); sqlcipher_set_hmac_salt_mask(mask); } } else { char *hmac_salt_mask = sqlite3_mprintf("%02x", sqlcipher_get_hmac_salt_mask()); codec_vdbe_return_static_string(pParse, "cipher_hmac_salt_mask", hmac_salt_mask); sqlite3_free(hmac_salt_mask); } } }else { return 0; } return 1; }
/* sqlite3_rekey_v2 ** Given a database, this will reencrypt the database using a new key. ** There is only one possible modes of operation - to encrypt a database ** that is already encrpyted. If the database is not already encrypted ** this should do nothing ** The proposed logic for this function follows: ** 1. Determine if the database is already encryptped ** 2. If there is NOT already a key present do nothing ** 3. If there is a key present, re-encrypt the database with the new key */ int sqlite3_rekey_v2(sqlite3 *db, const char *zDb, const void *pKey, int nKey) { CODEC_TRACE(("sqlite3_rekey_v2: entered db=%p zDb=%s pKey=%s, nKey=%d\n", db, zDb, (char *)pKey, nKey)); if(db && pKey && nKey) { int db_index = sqlcipher_find_db_index(db, zDb); struct Db *pDb = &db->aDb[db_index]; CODEC_TRACE(("sqlite3_rekey_v2: database pDb=%p db_index:%d\n", pDb, db_index)); if(pDb->pBt) { codec_ctx *ctx; int rc, page_count; Pgno pgno; PgHdr *page; Pager *pPager = pDb->pBt->pBt->pPager; sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx); if(ctx == NULL) { /* there was no codec attached to this database, so this should do nothing! */ CODEC_TRACE(("sqlite3_rekey_v2: no codec attached to db, exiting\n")); return SQLITE_OK; } sqlite3_mutex_enter(db->mutex); codec_set_pass_key(db, db_index, pKey, nKey, CIPHER_WRITE_CTX); /* do stuff here to rewrite the database ** 1. Create a transaction on the database ** 2. Iterate through each page, reading it and then writing it. ** 3. If that goes ok then commit and put ctx->rekey into ctx->key ** note: don't deallocate rekey since it may be used in a subsequent iteration */ rc = sqlite3BtreeBeginTrans(pDb->pBt, 1); /* begin write transaction */ sqlite3PagerPagecount(pPager, &page_count); for(pgno = 1; rc == SQLITE_OK && pgno <= (unsigned int)page_count; pgno++) { /* pgno's start at 1 see pager.c:pagerAcquire */ if(!sqlite3pager_is_mj_pgno(pPager, pgno)) { /* skip this page (see pager.c:pagerAcquire for reasoning) */ rc = sqlite3PagerGet(pPager, pgno, &page); if(rc == SQLITE_OK) { /* write page see pager_incr_changecounter for example */ rc = sqlite3PagerWrite(page); if(rc == SQLITE_OK) { sqlite3PagerUnref(page); } else { CODEC_TRACE(("sqlite3_rekey_v2: error %d occurred writing page %d\n", rc, pgno)); } } else { CODEC_TRACE(("sqlite3_rekey_v2: error %d occurred getting page %d\n", rc, pgno)); } } } /* if commit was successful commit and copy the rekey data to current key, else rollback to release locks */ if(rc == SQLITE_OK) { CODEC_TRACE(("sqlite3_rekey_v2: committing\n")); rc = sqlite3BtreeCommit(pDb->pBt); sqlcipher_codec_key_copy(ctx, CIPHER_WRITE_CTX); } else { CODEC_TRACE(("sqlite3_rekey_v2: rollback\n")); sqlite3BtreeRollback(pDb->pBt, SQLITE_ABORT_ROLLBACK); } sqlite3_mutex_leave(db->mutex); } return SQLITE_OK; } return SQLITE_ERROR; }
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; }
/* sqlite3_rekey ** Given a database, this will reencrypt the database using a new key. ** There are two possible modes of operation. The first is rekeying ** an existing database that was not previously encrypted. The second ** is to change the key on an existing database. ** ** The proposed logic for this function follows: ** 1. Determine if there is already a key present ** 2. If there is NOT already a key present, create one and attach a codec (key would be null) ** 3. Initialize a ctx->rekey parameter of the codec ** ** Note: this will require modifications to the sqlite3Codec to support rekey ** */ int sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey) { CODEC_TRACE(("sqlite3_rekey: entered db=%d pKey=%s, nKey=%d\n", db, pKey, nKey)); activate_openssl(); if(db && pKey && nKey) { struct Db *pDb = &db->aDb[0]; CODEC_TRACE(("sqlite3_rekey: database pDb=%d\n", pDb)); if(pDb->pBt) { codec_ctx *ctx; int rc, page_count; Pgno pgno; PgHdr *page; Pager *pPager = pDb->pBt->pBt->pPager; sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx); if(ctx == NULL) { CODEC_TRACE(("sqlite3_rekey: no codec attached to db, attaching now\n")); /* there was no codec attached to this database,so attach one now with a null password */ sqlite3CodecAttach(db, 0, pKey, nKey); sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx); /* prepare this setup as if it had already been initialized */ RAND_pseudo_bytes(ctx->kdf_salt, ctx->kdf_salt_sz); ctx->read_ctx->key_sz = ctx->read_ctx->iv_sz = ctx->read_ctx->pass_sz = 0; } sqlite3_mutex_enter(db->mutex); if(ctx->read_ctx->iv_sz != ctx->write_ctx->iv_sz) { char *error; CODEC_TRACE(("sqlite3_rekey: updating page size for iv_sz change from %d to %d\n", ctx->read_ctx->iv_sz, ctx->write_ctx->iv_sz)); db->nextPagesize = SQLITE_DEFAULT_PAGE_SIZE; pDb->pBt->pBt->pageSizeFixed = 0; /* required for sqlite3BtreeSetPageSize to modify pagesize setting */ sqlite3BtreeSetPageSize(pDb->pBt, db->nextPagesize, EVP_MAX_IV_LENGTH, 0); sqlite3RunVacuum(&error, db); } codec_set_pass_key(db, 0, pKey, nKey, 1); ctx->mode_rekey = 1; /* do stuff here to rewrite the database ** 1. Create a transaction on the database ** 2. Iterate through each page, reading it and then writing it. ** 3. If that goes ok then commit and put ctx->rekey into ctx->key ** note: don't deallocate rekey since it may be used in a subsequent iteration */ rc = sqlite3BtreeBeginTrans(pDb->pBt, 1); /* begin write transaction */ sqlite3PagerPagecount(pPager, &page_count); for(pgno = 1; rc == SQLITE_OK && pgno <= page_count; pgno++) { /* pgno's start at 1 see pager.c:pagerAcquire */ if(!sqlite3pager_is_mj_pgno(pPager, pgno)) { /* skip this page (see pager.c:pagerAcquire for reasoning) */ rc = sqlite3PagerGet(pPager, pgno, &page); if(rc == SQLITE_OK) { /* write page see pager_incr_changecounter for example */ rc = sqlite3PagerWrite(page); //printf("sqlite3PagerWrite(%d)\n", pgno); if(rc == SQLITE_OK) { sqlite3PagerUnref(page); } } } } /* if commit was successful commit and copy the rekey data to current key, else rollback to release locks */ if(rc == SQLITE_OK) { CODEC_TRACE(("sqlite3_rekey: committing\n")); db->nextPagesize = SQLITE_DEFAULT_PAGE_SIZE; rc = sqlite3BtreeCommit(pDb->pBt); cipher_ctx_copy(ctx->read_ctx, ctx->write_ctx); } else { CODEC_TRACE(("sqlite3_rekey: rollback\n")); sqlite3BtreeRollback(pDb->pBt); } ctx->mode_rekey = 0; sqlite3_mutex_leave(db->mutex); } return SQLITE_OK; } return SQLITE_ERROR; }
/* sqlite3_rekey ** Given a database, this will reencrypt the database using a new key. ** There are two possible modes of operation. The first is rekeying ** an existing database that was not previously encrypted. The second ** is to change the key on an existing database. ** ** The proposed logic for this function follows: ** 1. Determine if there is already a key present ** 2. If there is NOT already a key present, create one and attach a codec (key would be null) ** 3. Initialize a ctx->rekey parameter of the codec ** ** Note: this will require modifications to the sqlite3Codec to support rekey ** */ int sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey) { if(db && pKey && nKey) { int i, prepared_key_sz; int key_sz = EVP_CIPHER_key_length(CIPHER); void *key = sqlite3Malloc(key_sz); if(key == NULL) return SQLITE_NOMEM; for(i=0; i<db->nDb; i++){ struct Db *pDb = &db->aDb[i]; if(pDb->pBt) { codec_ctx *ctx; int rc, page_count; Pgno pgno; PgHdr *page; Pager *pPager = pDb->pBt->pBt->pPager; sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx); if(ctx == NULL) { /* there was no codec attached to this database,so attach one now with a null password */ char *error; db->nextPagesize = sqlite3BtreeGetPageSize(pDb->pBt); pDb->pBt->pBt->pageSizeFixed = 0; /* required for sqlite3BtreeSetPageSize to modify pagesize setting */ sqlite3BtreeSetPageSize(pDb->pBt, db->nextPagesize, EVP_CIPHER_iv_length(CIPHER), 0); sqlite3RunVacuum(&error, db); sqlite3CodecAttach(db, i, pKey, nKey); sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx); /* prepare this setup as if it had already been initialized */ RAND_pseudo_bytes(ctx->salt, FILE_HEADER_SZ); ctx->rekey_plaintext = 1; } codec_prepare_key(db, pKey, nKey, ctx->salt, FILE_HEADER_SZ, key, &prepared_key_sz); assert(prepared_key_sz == key_sz); ctx->rekey = key; /* set rekey to new key data - note that ctx->key is original encryption key */ /* do stuff here to rewrite the database ** 1. Create a transaction on the database ** 2. Iterate through each page, reading it and then writing it. ** 3. If that goes ok then commit and put ctx->rekey into ctx->key ** note: don't deallocate rekey since it may be used in a subsequent iteration */ rc = sqlite3BtreeBeginTrans(pDb->pBt, 1); /* begin write transaction */ rc = sqlite3PagerPagecount(pPager, &page_count); for(pgno = 1; rc == SQLITE_OK && pgno <= page_count; pgno++) { /* pgno's start at 1 see pager.c:pagerAcquire */ if(!sqlite3pager_is_mj_pgno(pPager, pgno)) { /* skip this page (see pager.c:pagerAcquire for reasoning) */ rc = sqlite3PagerGet(pPager, pgno, &page); if(rc == SQLITE_OK) { /* write page see pager_incr_changecounter for example */ rc = sqlite3PagerWrite(page); //printf("sqlite3PagerWrite(%d)\n", pgno); if(rc == SQLITE_OK) { sqlite3PagerUnref(page); } } } } /* if commit was successful commit and copy the rekey data to current key, else rollback to release locks */ if(rc == SQLITE_OK) { rc = sqlite3BtreeCommit(pDb->pBt); memcpy(ctx->key, ctx->rekey, key_sz); if(ctx->pass) { memset(ctx->pass, 0, ctx->pass_sz); sqlite3_free(ctx->pass); } ctx->pass = sqlite3Malloc(nKey); if(ctx->pass == NULL) return SQLITE_NOMEM; memcpy(ctx->pass, pKey, nKey); ctx->pass_sz = nKey; } else { printf("error\n"); sqlite3BtreeRollback(pDb->pBt); } /* cleanup rekey data, make sure to overwrite rekey_plaintext or read errors will ensue */ ctx->rekey = NULL; ctx->rekey_plaintext = 0; } } /* clear and free temporary key data */ memset(key, 0, key_sz); sqlite3_free(key); return SQLITE_OK; } return SQLITE_ERROR; }