int sqlite3CodecAttach(sqlite3* db, int nDb, const void *zKey, int nKey) { struct Db *pDb = &db->aDb[nDb]; CODEC_TRACE(("sqlite3CodecAttach: entered nDb=%d zKey=%s, nKey=%d\n", nDb, zKey, nKey)); sqlcipher_activate(); if(nKey && zKey && pDb->pBt) { int rc; Pager *pPager = pDb->pBt->pBt->pPager; sqlite3_file *fd = sqlite3Pager_get_fd(pPager); codec_ctx *ctx; /* point the internal codec argument against the contet to be prepared */ rc = sqlcipher_codec_ctx_init(&ctx, pDb, pDb->pBt->pBt->pPager, fd, zKey, nKey); sqlite3pager_sqlite3PagerSetCodec(sqlite3BtreePager(pDb->pBt), sqlite3Codec, NULL, sqlite3FreeCodecArg, (void *) ctx); codec_set_btree_to_codec_pagesize(db, pDb, ctx); /* if fd is null, then this is an in-memory database and we dont' want to overwrite the AutoVacuum settings if not null, then set to the default */ sqlite3_mutex_enter(db->mutex); if(fd != NULL) { sqlite3BtreeSetAutoVacuum(pDb->pBt, SQLITE_DEFAULT_AUTOVACUUM); } sqlite3_mutex_leave(db->mutex); } return SQLITE_OK; }
int sqlite3CodecAttach(sqlite3* db, int nDb, const void *zKey, int nKey) { struct Db *pDb = &db->aDb[nDb]; CODEC_TRACE(("sqlite3CodecAttach: entered nDb=%d zKey=%s, nKey=%d\n", nDb, (char *)zKey, nKey)); if(nKey && zKey && pDb->pBt) { int rc; Pager *pPager = pDb->pBt->pBt->pPager; sqlite3_file *fd = sqlite3Pager_get_fd(pPager); codec_ctx *ctx; sqlcipher_activate(); /* perform internal initialization for sqlcipher */ sqlite3_mutex_enter(db->mutex); /* point the internal codec argument against the contet to be prepared */ rc = sqlcipher_codec_ctx_init(&ctx, pDb, pDb->pBt->pBt->pPager, fd, zKey, nKey); if(rc != SQLITE_OK) return rc; /* initialization failed, do not attach potentially corrupted context */ sqlite3pager_sqlite3PagerSetCodec(sqlite3BtreePager(pDb->pBt), sqlite3Codec, NULL, sqlite3FreeCodecArg, (void *) ctx); codec_set_btree_to_codec_pagesize(db, pDb, ctx); /* force secure delete. This has the benefit of wiping internal data when deleted and also ensures that all pages are written to disk (i.e. not skipped by sqlite3PagerDontWrite optimizations) */ sqlite3BtreeSecureDelete(pDb->pBt, 1); /* if fd is null, then this is an in-memory database and we dont' want to overwrite the AutoVacuum settings if not null, then set to the default */ if(fd != NULL) { sqlite3BtreeSetAutoVacuum(pDb->pBt, SQLITE_DEFAULT_AUTOVACUUM); } sqlite3_mutex_leave(db->mutex); } return SQLITE_OK; }
/* sqlite3_rekey ** 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(sqlite3 *db, const void *pKey, int nKey) { CODEC_TRACE(("sqlite3_rekey: entered db=%d pKey=%s, nKey=%d\n", db, pKey, nKey)); sqlcipher_activate(); 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) { /* there was no codec attached to this database, so this should do nothing! */ CODEC_TRACE(("sqlite3_rekey: no codec attached to db, exiting\n")); return SQLITE_OK; } sqlite3_mutex_enter(db->mutex); codec_set_pass_key(db, 0, 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 <= 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")); rc = sqlite3BtreeCommit(pDb->pBt); sqlcipher_codec_key_copy(ctx, CIPHER_WRITE_CTX); } else { CODEC_TRACE(("sqlite3_rekey: rollback\n")); sqlite3BtreeRollback(pDb->pBt); } sqlite3_mutex_leave(db->mutex); } return SQLITE_OK; } return SQLITE_ERROR; }
int sqliterkCryptoSetCipher(sqliterk_pager *pager, sqliterk_file *fd, const sqliterk_cipher_conf *conf) { codec_ctx *codec = NULL; int rc; if (conf) { // Check arguments. if (!conf->key || conf->key_len <= 0) return SQLITERK_MISUSE; // SQLite library must be initialized before calling sqlcipher_activate(), // or it will cause a deadlock. sqlite3_initialize(); sqlcipher_activate(); // XXX: fake BTree structure passed to sqlcipher_codec_ctx_init. // Member of such structure is assigned but never used by repair kit. int fake_db[8]; sqlite3_file_rkredir file; struct sqlite3_io_methods methods = {0}; methods.xRead = sqliterkRead; file.pMethods = &methods; file.fd = fd; file.kdf_salt = conf->kdf_salt; // Initialize codec context. rc = sqlcipher_codec_ctx_init(&codec, fake_db, NULL, &file, conf->key, conf->key_len); if (rc != SQLITE_OK) goto bail_sqlite_errstr; // Set cipher. if (conf->cipher_name) { rc = sqlcipher_codec_ctx_set_cipher(codec, conf->cipher_name, CIPHER_READWRITE_CTX); if (rc != SQLITE_OK) goto bail_sqlite_errstr; } // Set page size. if (conf->page_size > 0) { rc = sqlcipher_codec_ctx_set_pagesize(codec, conf->page_size); if (rc != SQLITE_OK) goto bail_sqlite_errstr; } // Set HMAC usage. if (conf->use_hmac >= 0) { rc = sqlcipher_codec_ctx_set_use_hmac(codec, conf->use_hmac); if (rc != SQLITE_OK) goto bail_sqlite_errstr; } // Set KDF Iteration. if (conf->kdf_iter > 0) { rc = sqlcipher_codec_ctx_set_kdf_iter(codec, conf->kdf_iter, CIPHER_READWRITE_CTX); if (rc != SQLITE_OK) goto bail; } // Update pager page size. int page_sz = sqlcipher_codec_ctx_get_pagesize(codec); int reserve_sz = sqlcipher_codec_ctx_get_reservesize(codec); pager->pagesize = page_sz; pager->reservedBytes = reserve_sz; } if (pager->codec) { sqlcipher_codec_ctx_free(&pager->codec); sqlcipher_deactivate(); } pager->codec = codec; return SQLITERK_OK; bail_sqlite_errstr: sqliterkOSError(SQLITERK_CANTOPEN, "Failed to initialize cipher context: %s", sqlite3_errstr(rc)); rc = SQLITERK_CANTOPEN; bail: if (codec) sqlcipher_codec_ctx_free(&codec); sqlcipher_deactivate(); return rc; }