support BLOB syntax for specifying binary keys

This commit is contained in:
Stephen Lombardo 2009-04-22 16:22:08 -04:00
parent 1d25ff34fe
commit 0cc240ac78
3 changed files with 78 additions and 35 deletions

4
README
View File

@ -40,7 +40,7 @@ use this method it is your responsibility to ensure that the data you provide a
64 character hex string, which will be converted directly to 32 bytes (256 bits) of
key data.
PRAGMA hexkey = '2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99';
PRAGMA key = "x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'";
To encrypt a database programatically you can use the sqlite3_key function. The data provided
in pKey is converted to an encryption key according to the same rules as PRAGMA key.
@ -59,7 +59,7 @@ after you've supplied the correct database password;
The hexrekey pragma may be used to rekey to a specific binary value
PRAGMA hexrekey = '2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99';
PRAGMA rekey = "x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'";
This can be accomplished programtically by using sqlite3_rekey;

View File

@ -55,6 +55,7 @@ typedef struct {
Btree *pBt;
} codec_ctx;
static int convertnosalt = 0;
/*
* The following two functions PKCS5_PBKDF2_HMAC_SHA256 and h__dump implement a
@ -129,11 +130,18 @@ static int PKCS5_PBKDF2_HMAC_SHA256(const char *pass, int passlen,
return 1;
}
static void codec_prepare_key(const void *zKey, int nKey, void *salt, int nSalt, void *out, int *nOut) {
static void codec_prepare_key(sqlite3 *db, const void *zKey, int nKey, void *salt, int nSalt, void *out, int *nOut) {
/* if key data lenth is exactly 256 bits / 32 bytes use the data directly */
if (nKey == 32) {
memcpy(out, zKey, nKey);
*nOut = nKey;
if (nKey == 35 && sqlite3StrNICmp(zKey ,"x'", 2) == 0) {
int n = nKey - 3; /* adjust for leading x' and tailing ' */
int half_n = n/2;
const char *z = zKey + 2; /* adjust lead offset of x' */
void *key = sqlite3HexToBlob(db, z, n);
memcpy(out, key, half_n);
*nOut = half_n;
memset(key, 0, half_n); /* cleanup temporary key data */
sqlite3DbFree(db, key);
fprintf(stderr, "\nusing hex key\n");
/* otherwise the key is provided as a string so hash it to get key data */
} else {
*nOut = SHA_DIGEST_LENGTH;
@ -216,7 +224,7 @@ void* sqlite3Codec(void *iCtx, void *pData, Pgno pgno, int mode) {
break;
}
if(pgno == 1 ) {
if(pgno == 1 && !convertnosalt) {
/* if this is a read & decrypt operation on the first page then copy the
first 16 bytes off the page into the context's random salt buffer
*/
@ -232,6 +240,10 @@ void* sqlite3Codec(void *iCtx, void *pData, Pgno pgno, int mode) {
codec_cipher(ctx, pgno, emode, pg_sz, pData, ctx->buffer);
}
if(emode == CIPHER_DECRYPT && convertnosalt) {
convertnosalt = 0;
}
if(emode == CIPHER_ENCRYPT) {
return ctx->buffer; /* return persistent buffer data, pData remains intact */
} else {
@ -284,7 +296,7 @@ int sqlite3CodecAttach(sqlite3* db, int nDb, const void *zKey, int nKey) {
RAND_pseudo_bytes(ctx->salt, FILE_HEADER_SZ);
}
codec_prepare_key(zKey, nKey, ctx->salt, FILE_HEADER_SZ, ctx->key, &prepared_key_sz);
codec_prepare_key(db, zKey, nKey, ctx->salt, FILE_HEADER_SZ, ctx->key, &prepared_key_sz);
assert(prepared_key_sz == ctx->key_sz);
sqlite3BtreeSetPageSize(ctx->pBt, sqlite3BtreeGetPageSize(ctx->pBt), ctx->iv_sz, 0);
@ -330,6 +342,9 @@ int sqlite3FreeCodecArg(void *pCodecArg) {
void sqlite3_activate_see(const char* in) {
/* do nothing, security enhancements are always active */
if(sqlite3StrICmp(in, "convertnosalt")==0) {
convertnosalt = 1;
}
}
int sqlite3_key(sqlite3 *db, const void *pKey, int nKey) {
@ -390,7 +405,7 @@ int sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey) {
ctx->rekey_plaintext = 1;
}
codec_prepare_key(pKey, nKey, ctx->salt, FILE_HEADER_SZ, key, &prepared_key_sz);
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 */

View File

@ -39,6 +39,7 @@
file delete -force test.db
file delete -force test2.db
file delete -force test3.db
file delete -force test4.db
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@ -59,9 +60,36 @@ do_test codec-1.1 {
} {}
db close
# set an encryption key and create some basic data
# set an hex key create some basic data
# create table and insert operations should work
do_test codec-1.2 {
sqlite_orig db test.db
execsql {
PRAGMA key = "x'98483C6EB40B6C31A448C22A66DED3B5E5E8D5119CAC8327B655C8B5C4836481'";
CREATE table t1(a,b);
INSERT INTO t1 VALUES ('test1', 'test2');
}
} {}
db close
# close database, open it again with the same
# hex key. verify that the table is readable
# and the data just inserted is visible
do_test codec-1.3 {
sqlite_orig db test.db
execsql {
PRAGMA key = "x'98483C6EB40B6C31A448C22A66DED3B5E5E8D5119CAC8327B655C8B5C4836481'";
SELECT name FROM sqlite_master WHERE type='table';
SELECT * from t1;
}
} {t1 test1 test2}
db close
file delete -force test.db
# set an encryption key and create some basic data
# create table and insert operations should work
do_test codec-1.4 {
sqlite_orig db test.db
execsql {
PRAGMA key = 'testkey';
@ -74,7 +102,7 @@ db close
# close database, open it again with the same
# key. verify that the table is readable
# and the data just inserted is visible
do_test codec-1.3 {
do_test codec-1.5 {
sqlite_orig db test.db
execsql {
PRAGMA key = 'testkey';
@ -87,7 +115,7 @@ db close
# open the database and try to read from it without
# providing a passphrase. verify that the
# an error is returned from the library
do_test codec-1.4 {
do_test codec-1.6 {
sqlite_orig db test.db
catchsql {
SELECT name FROM sqlite_master WHERE type='table';
@ -98,7 +126,7 @@ db close
# open the database and try to set an invalid
# passphrase. verify that an error is returned
# and that data couldn't be read
do_test codec-1.5 {
do_test codec-1.7 {
sqlite_orig db test.db
catchsql {
PRAGMA key = 'testkey2';
@ -107,8 +135,18 @@ do_test codec-1.5 {
} {1 {file is encrypted or is not a database}}
db close
# test invalid hex key fails
do_test codec-1.8 {
sqlite_orig db test.db
catchsql {
PRAGMA key = "x'98483C6EB40B6C31A448C22A66DED3B5E5E8D5119CAC8327B655C8B5C4836480'";
SELECT name FROM sqlite_master WHERE type='table';
}
} {1 {file is encrypted or is not a database}}
db close
# test a large number of inserts in a transaction to a memory database
do_test codec-1.6 {
do_test codec-1.9 {
sqlite_orig db :memory:
execsql {
PRAGMA key = 'testkey3';
@ -128,18 +166,8 @@ do_test codec-1.6 {
} {25000 0}
db close
# test invalid hex key fails
do_test codec-1.7 {
sqlite_orig db test.db
catchsql {
PRAGMA hexkey = '98483C6EB40B6C31A448C22A66DED3B5E5E8D5119CAC8327B655C8B5C4836480';
SELECT name FROM sqlite_master WHERE type='table';
}
} {1 {file is encrypted or is not a database}}
db close
# test a large number of inserts in a transaction for multiple pages
do_test codec-1.8 {
do_test codec-1.10 {
sqlite_orig db test.db
execsql {
PRAGMA key = 'testkey';
@ -158,7 +186,7 @@ do_test codec-1.8 {
db close
# test initial rekey
do_test codec-1.9 {
do_test codec-1.11 {
sqlite_orig db test.db
execsql {
PRAGMA key = 'testkey';
@ -169,7 +197,7 @@ db close
# test that now the new key opens the database
# now close database re-open with new key
do_test codec-1.10 {
do_test codec-1.12 {
sqlite_orig db test.db
execsql {
PRAGMA key = 'testkeynew';
@ -179,7 +207,7 @@ do_test codec-1.10 {
db close
# test rekey on an unecrypted database
do_test codec-1.11 {
do_test codec-1.13 {
sqlite_orig db2 test2.db
execsql {
BEGIN;
@ -208,7 +236,7 @@ do_test codec-1.11 {
db2 close
# attach an encrypted database
do_test codec-1.12 {
do_test codec-1.14 {
sqlite_orig db3 test3.db
execsql {
@ -239,7 +267,7 @@ do_test codec-1.12 {
} {25000 15000}
db3 close
do_test codec-1.13 {
do_test codec-1.15 {
execsql {
BEGIN;
} db2
@ -260,7 +288,7 @@ do_test codec-1.13 {
db2 close
# test locking across multiple handles
do_test codec-1.14 {
do_test codec-1.16 {
sqlite_orig db3 test3.db
execsql {
@ -278,7 +306,7 @@ do_test codec-1.14 {
} {1 {database is locked}}
# test locks are released
do_test codec-1.15 {
do_test codec-1.17 {
execsql {
COMMIT;
} db3
@ -293,7 +321,7 @@ db3 close
db3a close
# alter schema
do_test codec-1.16 {
do_test codec-1.18 {
sqlite_orig db3 test3.db
execsql {
PRAGMA key = 'testkey';
@ -306,7 +334,7 @@ do_test codec-1.16 {
} {}
db3 close
do_test codec-1.17 {
do_test codec-1.19 {
sqlite_orig db3 test3.db
execsql {
PRAGMA key = 'testkey';