Skip to content

Commit

Permalink
Add PRAGMA cipher_store_pass
Browse files Browse the repository at this point in the history
PRAGMA cipher_store_pass allows the passphrase to remain in memory
which will allow an ATTACH statement to succeed without providing
the key value during ATTACH when both databases use the same passphrase,
but have different salt values.  Previously, in order to ATTACH with
this scneario, calling ATTACH required providing the key value inline.
  • Loading branch information
developernotes committed Jun 13, 2014
1 parent aa2e37e commit 90606b3
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 7 deletions.
21 changes: 16 additions & 5 deletions src/crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,16 @@ int sqlcipher_codec_pragma(sqlite3* db, int iDb, Parse *pParse, const char *zLef
}

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_profile")== 0 && zRight ){

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);
Expand Down Expand Up @@ -471,8 +478,12 @@ void sqlite3CodecGetKey(sqlite3* db, int nDb, void **zKey, int *nKey) {
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);
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;
Expand Down
4 changes: 4 additions & 0 deletions src/crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,10 @@ int sqlcipher_codec_ctx_migrate(codec_ctx *ctx);
int sqlcipher_codec_add_random(codec_ctx *ctx, const char *data, int random_sz);
int sqlcipher_cipher_profile(sqlite3 *db, const char *destination);
static void sqlcipher_profile_callback(void *file, const char *sql, sqlite3_uint64 run_time);
static int sqlcipher_codec_get_store_pass(codec_ctx *ctx);
static void sqlcipher_codec_get_pass(codec_ctx *ctx, void **zKey, int *nKey);
static void sqlcipher_codec_set_store_pass(codec_ctx *ctx, int value);

#endif
#endif
/* END SQLCIPHER */
20 changes: 18 additions & 2 deletions src/crypto_impl.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
to keep track of read / write state separately. The following
struct and associated functions are defined here */
typedef struct {
int store_pass;
int derive_key;
int kdf_iter;
int fast_kdf_iter;
Expand Down Expand Up @@ -401,6 +402,19 @@ static int sqlcipher_cipher_ctx_set_keyspec(cipher_ctx *ctx, const unsigned char
return SQLITE_OK;
}

static int sqlcipher_codec_get_store_pass(codec_ctx *ctx) {
return ctx->read_ctx->store_pass;
}

static void sqlcipher_codec_set_store_pass(codec_ctx *ctx, int value) {
ctx->read_ctx->store_pass = value;
}

static void sqlcipher_codec_get_pass(codec_ctx *ctx, void **zKey, int *nKey) {
*zKey = ctx->read_ctx->pass;
*nKey = ctx->read_ctx->pass_sz;
}

/**
* Set the passphrase for the cipher_ctx
*
Expand Down Expand Up @@ -897,8 +911,10 @@ int sqlcipher_codec_key_derive(codec_ctx *ctx) {
}

/* TODO: wipe and free passphrase after key derivation */
sqlcipher_cipher_ctx_set_pass(ctx->read_ctx, NULL, 0);
sqlcipher_cipher_ctx_set_pass(ctx->write_ctx, NULL, 0);
if(ctx->read_ctx->store_pass != 1) {
sqlcipher_cipher_ctx_set_pass(ctx->read_ctx, NULL, 0);
sqlcipher_cipher_ctx_set_pass(ctx->write_ctx, NULL, 0);
}

return SQLITE_OK;
}
Expand Down
43 changes: 43 additions & 0 deletions test/crypto.test
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,49 @@ db2 close
file delete -force test.db
file delete -force test2.db

# attach an encrypted database
# without specifying key, verify it attaches
# correctly when PRAGMA cipher_store_pass = 1
# is set.
do_test attach-database-with-default-key-using-cipher-store-pass {

sqlite_orig db1 test.db
execsql {
PRAGMA key = 'testkey';
CREATE TABLE t1(a,b);
INSERT INTO t1(a,b) VALUES('foo', 'bar');
} db1
db1 close

sqlite_orig db2 test2.db
execsql {
PRAGMA key = 'testkey';
CREATE TABLE t2(a,b);
INSERT INTO t2 VALUES ('test1', 'test2');
} db2
db2 close

sqlite_orig db1 test.db
execsql {
PRAGMA key = 'testkey';
PRAGMA cipher_store_pass = 1;
ATTACH DATABASE 'test2.db' as db2;
SELECT sqlcipher_export('db2');
DETACH DATABASE db2;
} db1
db1 close

sqlite_orig db2 test2.db
execsql {
PRAGMA key = 'testkey';
SELECT * FROM t1;
} db2

} {foo bar}
db2 close
file delete -force test.db
file delete -force test2.db

# attach an encrypted database
# where both database have the same
# key explicitly
Expand Down

0 comments on commit 90606b3

Please sign in to comment.