diff options
author | Hans-Christoph Steiner <hans@eds.org> | 2012-03-30 20:42:12 -0400 |
---|---|---|
committer | Hans-Christoph Steiner <hans@eds.org> | 2012-03-30 20:42:12 -0400 |
commit | 7bb481fda9ecb134804b49c2ce77ca28f7eea583 (patch) | |
tree | 31b520b9914d3e2453968abe375f2c102772c3dc /src/crypto.c |
Imported Upstream version 2.0.3
Diffstat (limited to 'src/crypto.c')
-rw-r--r-- | src/crypto.c | 345 |
1 files changed, 345 insertions, 0 deletions
diff --git a/src/crypto.c b/src/crypto.c new file mode 100644 index 0000000..5c8b2d6 --- /dev/null +++ b/src/crypto.c @@ -0,0 +1,345 @@ +/* +** SQLCipher +** crypto.c developed by Stephen Lombardo (Zetetic LLC) +** sjlombardo at zetetic dot net +** http://zetetic.net +** +** Copyright (c) 2009, ZETETIC LLC +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of the ZETETIC LLC nor the +** names of its contributors may be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY +** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +** DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY +** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +** +*/ +/* BEGIN CRYPTO */ +#ifdef SQLITE_HAS_CODEC + +#include <assert.h> +#include "sqliteInt.h" +#include "btreeInt.h" +#include "crypto.h" + +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; + sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx); + if(ctx) return sqlcipher_codec_ctx_set_kdf_iter(ctx, kdf_iter, 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); + if(ctx) return sqlcipher_codec_ctx_set_fast_kdf_iter(ctx, kdf_iter, for_ctx); + } + return SQLITE_ERROR; +} + +static int codec_set_btree_to_codec_pagesize(sqlite3 *db, Db *pDb, codec_ctx *ctx) { + int rc, page_sz, reserve_sz; + + page_sz = sqlcipher_codec_ctx_get_pagesize(ctx); + reserve_sz = sqlcipher_codec_ctx_get_reservesize(ctx); + + sqlite3_mutex_enter(db->mutex); + db->nextPagesize = page_sz; + pDb->pBt->pBt->pageSizeFixed = 0; + CODEC_TRACE(("codec_set_btree_to_codec_pagesize: sqlite3BtreeSetPageSize() size=%d reserve=%d\n", page_sz, reserve_sz)); + rc = sqlite3BtreeSetPageSize(pDb->pBt, page_sz, reserve_sz, 0); + sqlite3_mutex_leave(db->mutex); + return rc; +} + +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); + if(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; +} + +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); + + if(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; +} + +/** + * + * 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); + if(ctx) return sqlcipher_codec_ctx_set_cipher(ctx, cipher_name, for_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; + 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; +} + +/* + * sqlite3Codec can be called in multiple modes. + * encrypt mode - expected to return a pointer to the + * encrypted data without altering pData. + * decrypt mode - expected to return a pointer to pData, with + * the data decrypted in the input buffer + */ +void* sqlite3Codec(void *iCtx, void *data, Pgno pgno, int mode) { + codec_ctx *ctx = (codec_ctx *) iCtx; + int offset = 0, rc = 0; + int page_sz = sqlcipher_codec_ctx_get_pagesize(ctx); + unsigned char *pData = (unsigned char *) data; + void *buffer = sqlcipher_codec_ctx_get_data(ctx); + void *kdf_salt = sqlcipher_codec_ctx_get_kdf_salt(ctx); + CODEC_TRACE(("sqlite3Codec: entered pgno=%d, mode=%d, page_sz=%d\n", pgno, mode, page_sz)); + + /* call to derive keys if not present yet */ + if((rc = sqlcipher_codec_key_derive(ctx)) != SQLITE_OK) { + sqlcipher_codec_ctx_set_error(ctx, rc); + return NULL; + } + + if(pgno == 1) offset = FILE_HEADER_SZ; /* adjust starting pointers in data page for header offset on first page*/ + + CODEC_TRACE(("sqlite3Codec: switch mode=%d offset=%d\n", mode, offset)); + switch(mode) { + case 0: /* decrypt */ + case 2: + case 3: + if(pgno == 1) memcpy(buffer, SQLITE_FILE_HEADER, FILE_HEADER_SZ); /* copy file header to the first 16 bytes of the page */ + rc = sqlcipher_page_cipher(ctx, CIPHER_READ_CTX, pgno, CIPHER_DECRYPT, page_sz - offset, pData + offset, (unsigned char*)buffer + offset); + if(rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, rc); + memcpy(pData, buffer, page_sz); /* copy buffer data back to pData and return */ + return pData; + break; + case 6: /* encrypt */ + if(pgno == 1) memcpy(buffer, kdf_salt, FILE_HEADER_SZ); /* copy salt to output buffer */ + rc = sqlcipher_page_cipher(ctx, CIPHER_WRITE_CTX, pgno, CIPHER_ENCRYPT, page_sz - offset, pData + offset, (unsigned char*)buffer + offset); + if(rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, rc); + return buffer; /* return persistent buffer data, pData remains intact */ + break; + case 7: + if(pgno == 1) memcpy(buffer, kdf_salt, FILE_HEADER_SZ); /* copy salt to output buffer */ + rc = sqlcipher_page_cipher(ctx, CIPHER_READ_CTX, pgno, CIPHER_ENCRYPT, page_sz - offset, pData + offset, (unsigned char*)buffer + offset); + if(rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, rc); + return buffer; /* return persistent buffer data, pData remains intact */ + break; + default: + return pData; + break; + } +} + +void sqlite3FreeCodecArg(void *pCodecArg) { + codec_ctx *ctx = (codec_ctx *) pCodecArg; + if(pCodecArg == NULL) return; + sqlcipher_codec_ctx_free(&ctx); // wipe and free allocated memory for the context +} + +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; +} + +void sqlite3_activate_see(const char* in) { + /* do nothing, security enhancements are always active */ +} + +int sqlite3_key(sqlite3 *db, const void *pKey, int nKey) { + CODEC_TRACE(("sqlite3_key: entered db=%d pKey=%s nKey=%d\n", db, pKey, nKey)); + /* attach key if db and pKey are not null and nKey is > 0 */ + if(db && pKey && nKey) { + sqlite3CodecAttach(db, 0, pKey, nKey); // operate only on the main db + return SQLITE_OK; + } + return SQLITE_ERROR; +} + +/* 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; +} + +void sqlite3CodecGetKey(sqlite3* db, int nDb, void **zKey, int *nKey) { + struct Db *pDb = &db->aDb[nDb]; + CODEC_TRACE(("sqlite3CodecGetKey: entered db=%d, 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_pass(ctx, zKey, nKey); + } else { + *zKey = NULL; + *nKey = 0; + } + } +} + + +/* END CRYPTO */ +#endif |