summaryrefslogtreecommitdiff
path: root/src/crypto.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/crypto.c')
-rw-r--r--src/crypto.c222
1 files changed, 211 insertions, 11 deletions
diff --git a/src/crypto.c b/src/crypto.c
index 50213b5..2551e6b 100644
--- a/src/crypto.c
+++ b/src/crypto.c
@@ -1,10 +1,8 @@
/*
** SQLCipher
-** crypto.c developed by Stephen Lombardo (Zetetic LLC)
-** sjlombardo at zetetic dot net
-** http://zetetic.net
+** http://sqlcipher.net
**
-** Copyright (c) 2009, ZETETIC LLC
+** Copyright (c) 2008 - 2013, ZETETIC LLC
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
@@ -30,7 +28,7 @@
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
*/
-/* BEGIN CRYPTO */
+/* BEGIN SQLCIPHER */
#ifdef SQLITE_HAS_CODEC
#include <assert.h>
@@ -38,12 +36,12 @@
#include "btreeInt.h"
#include "crypto.h"
-const char* codec_get_cipher_version() {
+static const char* codec_get_cipher_version() {
return CIPHER_VERSION;
}
/* Generate code to return a string value */
-void codec_vdbe_return_static_string(Parse *pParse, const char *zLabel, const char *value){
+static void codec_vdbe_return_static_string(Parse *pParse, const char *zLabel, const char *value){
Vdbe *v = sqlite3GetVdbe(pParse);
sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLabel, SQLITE_STATIC);
@@ -69,7 +67,7 @@ static int codec_set_btree_to_codec_pagesize(sqlite3 *db, Db *pDb, codec_ctx *ct
return rc;
}
-int codec_set_pass_key(sqlite3* db, int nDb, const void *zKey, int nKey, int for_ctx) {
+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) {
@@ -91,6 +89,11 @@ int codec_pragma(sqlite3* db, int iDb, Parse *pParse, const char *zLeft, const c
CODEC_TRACE(("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_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
@@ -291,13 +294,13 @@ int sqlite3CodecAttach(sqlite3* db, int nDb, const void *zKey, int nKey) {
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 */
- sqlite3_mutex_enter(db->mutex);
-
sqlite3pager_sqlite3PagerSetCodec(sqlite3BtreePager(pDb->pBt), sqlite3Codec, NULL, sqlite3FreeCodecArg, (void *) ctx);
codec_set_btree_to_codec_pagesize(db, pDb, ctx);
@@ -423,6 +426,203 @@ void sqlite3CodecGetKey(sqlite3* db, int nDb, void **zKey, int *nKey) {
}
}
+#ifndef OMIT_EXPORT
+
+/*
+ * Implementation of an "export" function that allows a caller
+ * to duplicate the main database to an attached database. This is intended
+ * as a conveneince for users who need to:
+ *
+ * 1. migrate from an non-encrypted database to an encrypted database
+ * 2. move from an encrypted database to a non-encrypted database
+ * 3. convert beween the various flavors of encrypted databases.
+ *
+ * This implementation is based heavily on the procedure and code used
+ * in vacuum.c, but is exposed as a function that allows export to any
+ * named attached database.
+ */
+
+/*
+** Finalize a prepared statement. If there was an error, store the
+** text of the error message in *pzErrMsg. Return the result code.
+**
+** Based on vacuumFinalize from vacuum.c
+*/
+static int sqlcipher_finalize(sqlite3 *db, sqlite3_stmt *pStmt, char **pzErrMsg){
+ int rc;
+ rc = sqlite3VdbeFinalize((Vdbe*)pStmt);
+ if( rc ){
+ sqlite3SetString(pzErrMsg, db, sqlite3_errmsg(db));
+ }
+ return rc;
+}
+
+/*
+** Execute zSql on database db. Return an error code.
+**
+** Based on execSql from vacuum.c
+*/
+static int sqlcipher_execSql(sqlite3 *db, char **pzErrMsg, const char *zSql){
+ sqlite3_stmt *pStmt;
+ VVA_ONLY( int rc; )
+ if( !zSql ){
+ return SQLITE_NOMEM;
+ }
+ if( SQLITE_OK!=sqlite3_prepare(db, zSql, -1, &pStmt, 0) ){
+ sqlite3SetString(pzErrMsg, db, sqlite3_errmsg(db));
+ return sqlite3_errcode(db);
+ }
+ VVA_ONLY( rc = ) sqlite3_step(pStmt);
+ assert( rc!=SQLITE_ROW );
+ return sqlcipher_finalize(db, pStmt, pzErrMsg);
+}
+
+/*
+** Execute zSql on database db. The statement returns exactly
+** one column. Execute this as SQL on the same database.
+**
+** Based on execExecSql from vacuum.c
+*/
+static int sqlcipher_execExecSql(sqlite3 *db, char **pzErrMsg, const char *zSql){
+ sqlite3_stmt *pStmt;
+ int rc;
+
+ rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
+ if( rc!=SQLITE_OK ) return rc;
+
+ while( SQLITE_ROW==sqlite3_step(pStmt) ){
+ rc = sqlcipher_execSql(db, pzErrMsg, (char*)sqlite3_column_text(pStmt, 0));
+ if( rc!=SQLITE_OK ){
+ sqlcipher_finalize(db, pStmt, pzErrMsg);
+ return rc;
+ }
+ }
+
+ return sqlcipher_finalize(db, pStmt, pzErrMsg);
+}
+
+/*
+ * copy database and schema from the main database to an attached database
+ *
+ * Based on sqlite3RunVacuum from vacuum.c
+*/
+void sqlcipher_exportFunc(sqlite3_context *context, int argc, sqlite3_value **argv) {
+ sqlite3 *db = sqlite3_context_db_handle(context);
+ const char* attachedDb = (const char*) sqlite3_value_text(argv[0]);
+ int saved_flags; /* Saved value of the db->flags */
+ int saved_nChange; /* Saved value of db->nChange */
+ int saved_nTotalChange; /* Saved value of db->nTotalChange */
+ void (*saved_xTrace)(void*,const char*); /* Saved db->xTrace */
+ int rc = SQLITE_OK; /* Return code from service routines */
+ char *zSql = NULL; /* SQL statements */
+ char *pzErrMsg = NULL;
+
+ 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;
+
+ /* Query the schema of the main database. Create a mirror schema
+ ** in the temporary database.
+ */
+ zSql = sqlite3_mprintf(
+ "SELECT 'CREATE TABLE %s.' || substr(sql,14) "
+ " FROM sqlite_master WHERE type='table' AND name!='sqlite_sequence'"
+ " AND rootpage>0"
+ , attachedDb);
+ rc = (zSql == NULL) ? SQLITE_NOMEM : sqlcipher_execExecSql(db, &pzErrMsg, zSql);
+ if( rc!=SQLITE_OK ) goto end_of_export;
+ sqlite3_free(zSql);
+
+ zSql = sqlite3_mprintf(
+ "SELECT 'CREATE INDEX %s.' || substr(sql,14)"
+ " FROM sqlite_master WHERE sql LIKE 'CREATE INDEX %%' "
+ , attachedDb);
+ rc = (zSql == NULL) ? SQLITE_NOMEM : sqlcipher_execExecSql(db, &pzErrMsg, zSql);
+ if( rc!=SQLITE_OK ) goto end_of_export;
+ sqlite3_free(zSql);
+
+ zSql = sqlite3_mprintf(
+ "SELECT 'CREATE UNIQUE INDEX %s.' || substr(sql,21) "
+ " FROM sqlite_master WHERE sql LIKE 'CREATE UNIQUE INDEX %%'"
+ , attachedDb);
+ rc = (zSql == NULL) ? SQLITE_NOMEM : sqlcipher_execExecSql(db, &pzErrMsg, zSql);
+ if( rc!=SQLITE_OK ) goto end_of_export;
+ sqlite3_free(zSql);
+
+ /* Loop through the tables in the main database. For each, do
+ ** an "INSERT INTO rekey_db.xxx SELECT * FROM main.xxx;" to copy
+ ** the contents to the temporary database.
+ */
+ zSql = sqlite3_mprintf(
+ "SELECT 'INSERT INTO %s.' || quote(name) "
+ "|| ' SELECT * FROM main.' || quote(name) || ';'"
+ "FROM main.sqlite_master "
+ "WHERE type = 'table' AND name!='sqlite_sequence' "
+ " AND rootpage>0"
+ , attachedDb);
+ rc = (zSql == NULL) ? SQLITE_NOMEM : sqlcipher_execExecSql(db, &pzErrMsg, zSql);
+ if( rc!=SQLITE_OK ) goto end_of_export;
+ sqlite3_free(zSql);
+
+ /* Copy over the sequence table
+ */
+ zSql = sqlite3_mprintf(
+ "SELECT 'DELETE FROM %s.' || quote(name) || ';' "
+ "FROM %s.sqlite_master WHERE name='sqlite_sequence' "
+ , attachedDb, attachedDb);
+ rc = (zSql == NULL) ? SQLITE_NOMEM : sqlcipher_execExecSql(db, &pzErrMsg, zSql);
+ if( rc!=SQLITE_OK ) goto end_of_export;
+ sqlite3_free(zSql);
+
+ zSql = sqlite3_mprintf(
+ "SELECT 'INSERT INTO %s.' || quote(name) "
+ "|| ' SELECT * FROM main.' || quote(name) || ';' "
+ "FROM %s.sqlite_master WHERE name=='sqlite_sequence';"
+ , attachedDb, attachedDb);
+ rc = (zSql == NULL) ? SQLITE_NOMEM : sqlcipher_execExecSql(db, &pzErrMsg, zSql);
+ if( rc!=SQLITE_OK ) goto end_of_export;
+ sqlite3_free(zSql);
+
+ /* Copy the triggers, views, and virtual tables from the main database
+ ** over to the temporary database. None of these objects has any
+ ** associated storage, so all we have to do is copy their entries
+ ** from the SQLITE_MASTER table.
+ */
+ zSql = sqlite3_mprintf(
+ "INSERT INTO %s.sqlite_master "
+ " SELECT type, name, tbl_name, rootpage, sql"
+ " FROM main.sqlite_master"
+ " WHERE type='view' OR type='trigger'"
+ " OR (type='table' AND rootpage=0)"
+ , attachedDb);
+ rc = (zSql == NULL) ? SQLITE_NOMEM : sqlcipher_execSql(db, &pzErrMsg, zSql);
+ if( rc!=SQLITE_OK ) goto end_of_export;
+ sqlite3_free(zSql);
+
+ zSql = NULL;
+end_of_export:
+ db->flags = saved_flags;
+ db->nChange = saved_nChange;
+ db->nTotalChange = saved_nTotalChange;
+ db->xTrace = saved_xTrace;
+
+ sqlite3_free(zSql);
+
+ if(rc) {
+ if(pzErrMsg != NULL) {
+ sqlite3_result_error(context, pzErrMsg, -1);
+ sqlite3DbFree(db, pzErrMsg);
+ } else {
+ sqlite3_result_error(context, sqlite3ErrStr(rc), -1);
+ }
+ }
+}
+
+#endif
-/* END CRYPTO */
+/* END SQLCIPHER */
#endif