/* 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; }