improvements to pragmas, allow custom page sizes and page data checks via hmac

- now possible to set custom page size using PRAGMA cipher_page_size = N;
- allow custom pragmas to be used on attached databases
- perform hmac on page ciphertext and IV before decryption
This commit is contained in:
Stephen Lombardo
2011-02-16 16:07:20 -05:00
parent d6d64068d9
commit b62c74407a
3 changed files with 334 additions and 54 deletions
+186 -50
View File
@@ -56,13 +56,18 @@ typedef struct {
int key_sz;
int iv_sz;
int pass_sz;
int reserve_sz;
int hmac_sz;
int use_hmac;
unsigned char *key;
unsigned char *hmac_key;
char *pass;
} cipher_ctx;
typedef struct {
int kdf_salt_sz;
int mode_rekey;
int page_sz;
unsigned char *kdf_salt;
unsigned char *buffer;
Btree *pBt;
@@ -94,6 +99,15 @@ static void cipher_hex2bin(const char *hex, int sz, unsigned char *out){
}
}
static int fixed_time_memcmp(const unsigned char *a0, const unsigned char *a1, int len) {
int i = 0, noMatch = 0;
for(i = 0; i < len; i++) {
noMatch = (noMatch || (a0[i] != a1[i]));
}
return noMatch;
}
/**
* Free and wipe memory
@@ -140,7 +154,9 @@ static int cipher_ctx_init(cipher_ctx **iCtx) {
if(ctx == NULL) return SQLITE_NOMEM;
memset(ctx, 0, sizeof(cipher_ctx));
ctx->key = sqlite3Malloc(EVP_MAX_KEY_LENGTH);
ctx->hmac_key = sqlite3Malloc(EVP_MAX_KEY_LENGTH);
if(ctx->key == NULL) return SQLITE_NOMEM;
if(ctx->hmac_key == NULL) return SQLITE_NOMEM;
return SQLITE_OK;
}
@@ -151,6 +167,7 @@ static void cipher_ctx_free(cipher_ctx **iCtx) {
cipher_ctx *ctx = *iCtx;
CODEC_TRACE(("cipher_ctx_free: entered iCtx=%d\n", iCtx));
codec_free(ctx->key, ctx->key_sz);
codec_free(ctx->hmac_key, ctx->key_sz);
codec_free(ctx->pass, ctx->pass_sz);
codec_free(ctx, sizeof(cipher_ctx));
}
@@ -165,15 +182,22 @@ static void cipher_ctx_free(cipher_ctx **iCtx) {
*/
static int cipher_ctx_copy(cipher_ctx *target, cipher_ctx *source) {
void *key = target->key;
void *hmac_key = target->hmac_key;
CODEC_TRACE(("cipher_ctx_copy: entered target=%d, source=%d\n", target, source));
codec_free(target->pass, target->pass_sz);
memcpy(target, source, sizeof(cipher_ctx));
target->key = key; //restore pointer to previously allocated key data
memcpy(target->key, source->key, EVP_MAX_KEY_LENGTH);
target->hmac_key = hmac_key; //restore pointer to previously allocated hmac key data
memcpy(target->hmac_key, source->hmac_key, EVP_MAX_KEY_LENGTH);
target->pass = sqlite3Malloc(source->pass_sz);
if(target->pass == NULL) return SQLITE_NOMEM;
memcpy(target->pass, source->pass, source->pass_sz);
return SQLITE_OK;
}
@@ -194,7 +218,7 @@ static int cipher_ctx_cmp(cipher_ctx *c1, cipher_ctx *c2) {
&& c1->pass_sz == c2->pass_sz
&& (
c1->pass == c2->pass
|| !memcmp(c1->pass, c2->pass, c1->pass_sz)
|| !fixed_time_memcmp(c1->pass, c2->pass, c1->pass_sz)
)
) return 0;
return 1;
@@ -223,11 +247,13 @@ static void codec_ctx_free(codec_ctx **iCtx) {
* Otherwise, a key data will be derived using PBKDF2
*
* returns SQLITE_OK if initialization was successful
* returns SQLITE_NOMEM if the key could't be derived (for instance if pass is NULL or pass_sz is 0)
* returns SQLITE_ERROR if the key could't be derived (for instance if pass is NULL or pass_sz is 0)
*/
static int codec_key_derive(codec_ctx *ctx, cipher_ctx *c_ctx) {
CODEC_TRACE(("codec_key_derive: entered c_ctx->pass=%s, c_ctx->pass_sz=%d ctx->kdf_salt=%d ctx->kdf_salt_sz=%d c_ctx->kdf_iter=%d c_ctx->key_sz=%d\n",
c_ctx->pass, c_ctx->pass_sz, ctx->kdf_salt, ctx->kdf_salt_sz, c_ctx->kdf_iter, c_ctx->key_sz));
CODEC_TRACE(("codec_key_derive: entered c_ctx->pass=%s, c_ctx->pass_sz=%d \
ctx->kdf_salt=%d ctx->kdf_salt_sz=%d c_ctx->kdf_iter=%d c_ctx->key_sz=%d\n",
c_ctx->pass, c_ctx->pass_sz, ctx->kdf_salt, ctx->kdf_salt_sz,
c_ctx->kdf_iter, c_ctx->key_sz));
if(c_ctx->pass && c_ctx->pass_sz) { // if pass is not null
if (c_ctx->pass_sz == ((c_ctx->key_sz*2)+3) && sqlite3StrNICmp(c_ctx->pass ,"x'", 2) == 0) {
@@ -237,13 +263,42 @@ static int codec_key_derive(codec_ctx *ctx, cipher_ctx *c_ctx) {
cipher_hex2bin(z, n, c_ctx->key);
} else {
CODEC_TRACE(("codec_key_derive: deriving key using PBKDF2\n"));
PKCS5_PBKDF2_HMAC_SHA1(c_ctx->pass, c_ctx->pass_sz, ctx->kdf_salt, ctx->kdf_salt_sz, c_ctx->kdf_iter, c_ctx->key_sz, c_ctx->key);
PKCS5_PBKDF2_HMAC_SHA1( c_ctx->pass, c_ctx->pass_sz,
ctx->kdf_salt, ctx->kdf_salt_sz,
c_ctx->kdf_iter, c_ctx->key_sz, c_ctx->key);
}
/* if this context is setup to use hmac checks, generate a seperate and different
key for HMAC. In this case, we use the output of the previous KDF as the input to
this KDF run. This ensures a distinct but predictable HMAC key. */
if(c_ctx->use_hmac) {
CODEC_TRACE(("codec_key_derive: deriving hmac key using PBKDF2\n"));
PKCS5_PBKDF2_HMAC_SHA1( c_ctx->key, c_ctx->key_sz,
ctx->kdf_salt, ctx->kdf_salt_sz,
c_ctx->kdf_iter, c_ctx->key_sz, c_ctx->hmac_key);
}
return SQLITE_OK;
};
return SQLITE_ERROR;
}
static int codec_hmac(cipher_ctx *ctx, Pgno pgno, unsigned char *in, int in_sz, unsigned char *out) {
HMAC_CTX hctx;
HMAC_CTX_init(&hctx);
HMAC_Init_ex(&hctx, ctx->key, ctx->key_sz, EVP_sha1(), NULL);
/* include the encrypted page data, initialization vector, and page number in HMAC. This will
prevent both tampering with the ciphertext, manipulation of the IV, or resequencing otherwise
valid pages out of order in a database */
HMAC_Update(&hctx, in, in_sz);
HMAC_Update(&hctx, (const unsigned char*) &pgno, sizeof(Pgno));
HMAC_Final(&hctx, out, NULL);
HMAC_CTX_cleanup(&hctx);
}
/*
* ctx - codec context
* pgno - page number in database
@@ -252,10 +307,21 @@ static int codec_key_derive(codec_ctx *ctx, cipher_ctx *c_ctx) {
* in - pointer to input bytes
* out - pouter to output bytes
*/
static int codec_cipher(cipher_ctx *ctx, Pgno pgno, int mode, int size, unsigned char *in, unsigned char *out) {
static int codec_cipher(cipher_ctx *ctx, Pgno pgno, int mode, int page_sz, unsigned char *in, unsigned char *out) {
EVP_CIPHER_CTX ectx;
unsigned char *iv;
int tmp_csz, csz;
unsigned char *iv_in, *iv_out, *hmac_in, *hmac_out, *out_start;
int tmp_csz, csz, size;
/* calculate some required positions into various buffers */
size = page_sz - ctx->reserve_sz; /* adjust size to useable size and memset reserve at end of page */
iv_out = out + size;
iv_in = in + size;
/* hmac will be written immediately after the initialization vector. the remainder of the page reserve will contain
random bytes. note, these pointers are only valid when use_hmac is true */
hmac_in = in + size + ctx->iv_sz;
hmac_out = out + size + ctx->iv_sz;
out_start = out; /* note the original position of the output buffer pointer, as out will be rewritten during encryption */
CODEC_TRACE(("codec_cipher:entered pgno=%d, mode=%d, size=%d\n", pgno, mode, size));
@@ -266,18 +332,29 @@ static int codec_cipher(cipher_ctx *ctx, Pgno pgno, int mode, int size, unsigned
return SQLITE_OK;
}
// FIXME - only run if using an IV
size = size - ctx->iv_sz; /* adjust size to useable size and memset reserve at end of page */
iv = out + size;
if(mode == CIPHER_ENCRYPT) {
RAND_pseudo_bytes(iv, ctx->iv_sz);
} else {
memcpy(iv, in+size, ctx->iv_sz);
RAND_pseudo_bytes(iv_out, ctx->reserve_sz); /* start at front of the reserve block, write random data to the end */
} else { /* CIPHER_DECRYPT */
memcpy(iv_out, iv_in, ctx->iv_sz); /* copy the iv from the input to output buffer */
}
if(ctx->use_hmac && (mode == CIPHER_DECRYPT)) {
codec_hmac(ctx, pgno, in, size + ctx->iv_sz, hmac_out);
CODEC_TRACE(("codec_cipher: comparing hmac on in=%d out=%d hmac_sz=%d\n", hmac_in, hmac_out, ctx->hmac_sz));
if(fixed_time_memcmp(hmac_in, hmac_out, ctx->hmac_sz) != 0) {
/* the hmac check failed, which means the data was tampered with or
corrupted in some way. we will return an error, and zero out the page data
to force an error */
memset(out, 0, page_sz);
CODEC_TRACE(("codec_cipher: hmac check failed for pgno=%d\n", pgno));
return SQLITE_ERROR;
}
}
EVP_CipherInit(&ectx, ctx->evp_cipher, NULL, NULL, mode);
EVP_CIPHER_CTX_set_padding(&ectx, 0);
EVP_CipherInit(&ectx, NULL, ctx->key, iv, mode);
EVP_CipherInit(&ectx, NULL, ctx->key, iv_out, mode);
EVP_CipherUpdate(&ectx, out, &tmp_csz, in, size);
csz = tmp_csz;
out += tmp_csz;
@@ -286,6 +363,10 @@ static int codec_cipher(cipher_ctx *ctx, Pgno pgno, int mode, int size, unsigned
EVP_CIPHER_CTX_cleanup(&ectx);
assert(size == csz);
if(ctx->use_hmac && (mode == CIPHER_ENCRYPT)) {
codec_hmac(ctx, pgno, out_start, size + ctx->iv_sz, hmac_out);
}
return SQLITE_OK;
}
@@ -308,6 +389,71 @@ int codec_set_kdf_iter(sqlite3* db, int nDb, int kdf_iter, int for_ctx) {
return SQLITE_ERROR;
}
int codec_set_use_hmac(sqlite3* db, int nDb, int use) {
int reserve;
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) {
codec_ctx *ctx;
sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx);
reserve = EVP_MAX_IV_LENGTH; /* base reserve size will be IV only */
/* calculate the amount of reserve needed to include an hmac and pad so that it is evenly
divisible by the max IV size */
if(use) {
int md_size = ctx->read_ctx->hmac_sz;
reserve += ((md_size % EVP_MAX_IV_LENGTH) == 0)
? md_size
: ((md_size / EVP_MAX_IV_LENGTH) + 1) * EVP_MAX_IV_LENGTH;
CODEC_TRACE(("codec_set_use_hmac: EVP_MAX_IV_LENGTH=%d md_size=%d reserve=%d\n",
EVP_MAX_IV_LENGTH, md_size, reserve));
}
ctx->write_ctx->use_hmac = ctx->read_ctx->use_hmac = use;
ctx->write_ctx->reserve_sz = ctx->read_ctx->reserve_sz = reserve;
/* since the use of hmac has changed, the page size has also changed */
return codec_set_page_size(db, nDb, ctx->page_sz);
}
return SQLITE_ERROR;
}
int codec_set_page_size(sqlite3* db, int nDb, int size) {
int rc;
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) {
codec_ctx *ctx;
sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx);
/* attempt to free the existing page bugger */
codec_free(ctx->buffer,ctx->page_sz);
ctx->page_sz = size;
/* pre-allocate a page buffer of PageSize bytes. This will
be used as a persistent buffer for encryption and decryption
operations to avoid overhead of multiple memory allocations*/
ctx->buffer = sqlite3Malloc(size);
if(ctx->buffer == NULL) return SQLITE_NOMEM;
/* Note: before forcing the page size we need to force pageSizeFixed to 0, else
sqliteBtreeSetPageSize will block the change */
sqlite3_mutex_enter(db->mutex);
db->nextPagesize = size;
pDb->pBt->pBt->pageSizeFixed = 0;
CODEC_TRACE(("codec_set_page_size: sqlite3BtreeSetPageSize() size=%d reserve=%d\n", size, ctx->read_ctx->reserve_sz));
rc = sqlite3BtreeSetPageSize(pDb->pBt, size, ctx->read_ctx->reserve_sz, 0);
sqlite3_mutex_leave(db->mutex);
return rc;
}
return SQLITE_ERROR;
}
/**
*
* when for_ctx == 0 then it will change for read
@@ -327,6 +473,8 @@ int codec_set_cipher_name(sqlite3* db, int nDb, const char *cipher_name, int for
c_ctx->evp_cipher = (EVP_CIPHER *) EVP_get_cipherbyname(cipher_name);
c_ctx->key_sz = EVP_CIPHER_key_length(c_ctx->evp_cipher);
c_ctx->iv_sz = EVP_CIPHER_iv_length(c_ctx->evp_cipher);
c_ctx->hmac_sz = EVP_MD_size(EVP_sha1());
c_ctx->derive_key = 1;
if(for_ctx == 2) cipher_ctx_copy( for_ctx ? ctx->read_ctx : ctx->write_ctx, c_ctx);
@@ -362,11 +510,10 @@ int codec_set_pass_key(sqlite3* db, int nDb, const void *zKey, int nKey, int for
*/
void* sqlite3Codec(void *iCtx, void *data, Pgno pgno, int mode) {
codec_ctx *ctx = (codec_ctx *) iCtx;
int pg_sz = SQLITE_DEFAULT_PAGE_SIZE;
int offset = 0;
int offset = 0, rc = 0;
unsigned char *pData = (unsigned char *) data;
CODEC_TRACE(("sqlite3Codec: entered pgno=%d, mode=%d, ctx->mode_rekey=%d, pg_sz=%d\n", pgno, mode, ctx->mode_rekey, pg_sz));
CODEC_TRACE(("sqlite3Codec: entered pgno=%d, mode=%d, ctx->mode_rekey=%d, page_sz=%d\n", pgno, mode, ctx->mode_rekey, ctx->page_sz));
/* derive key on first use if necessary */
if(ctx->read_ctx->derive_key) {
@@ -392,18 +539,21 @@ void* sqlite3Codec(void *iCtx, void *data, Pgno pgno, int mode) {
case 2:
case 3:
if(pgno == 1) memcpy(ctx->buffer, SQLITE_FILE_HEADER, FILE_HEADER_SZ); /* copy file header to the first 16 bytes of the page */
codec_cipher(ctx->read_ctx, pgno, CIPHER_DECRYPT, pg_sz - offset, pData + offset, ctx->buffer + offset);
memcpy(pData, ctx->buffer, pg_sz); /* copy buffer data back to pData and return */
rc = codec_cipher(ctx->read_ctx, pgno, CIPHER_DECRYPT, ctx->page_sz - offset, pData + offset, ctx->buffer + offset);
if(rc != SQLITE_OK) ctx->pBt->db->errCode = rc;
memcpy(pData, ctx->buffer, ctx->page_sz); /* copy buffer data back to pData and return */
return pData;
break;
case 6: /* encrypt */
if(pgno == 1) memcpy(ctx->buffer, ctx->kdf_salt, FILE_HEADER_SZ); /* copy salt to output buffer */
codec_cipher(ctx->write_ctx, pgno, CIPHER_ENCRYPT, pg_sz - offset, pData + offset, ctx->buffer + offset);
rc = codec_cipher(ctx->write_ctx, pgno, CIPHER_ENCRYPT, ctx->page_sz - offset, pData + offset, ctx->buffer + offset);
if(rc != SQLITE_OK) ctx->pBt->db->errCode = rc;
return ctx->buffer; /* return persistent buffer data, pData remains intact */
break;
case 7:
if(pgno == 1) memcpy(ctx->buffer, ctx->kdf_salt, FILE_HEADER_SZ); /* copy salt to output buffer */
codec_cipher(ctx->read_ctx, pgno, CIPHER_ENCRYPT, pg_sz - offset, pData + offset, ctx->buffer + offset);
rc = codec_cipher(ctx->read_ctx, pgno, CIPHER_ENCRYPT, ctx->page_sz - offset, pData + offset, ctx->buffer + offset);
if(rc != SQLITE_OK) ctx->pBt->db->errCode = rc;
return ctx->buffer; /* return persistent buffer data, pData remains intact */
break;
default:
@@ -428,18 +578,18 @@ int sqlite3CodecAttach(sqlite3* db, int nDb, const void *zKey, int nKey) {
ctx = sqlite3Malloc(sizeof(codec_ctx));
if(ctx == NULL) return SQLITE_NOMEM;
memset(ctx, 0, sizeof(codec_ctx)); /* initialize all pointers and values to 0 */
ctx->pBt = pDb->pBt; /* assign pointer to database btree structure */
/*
Always overwrite page size and set to the default because the first page of the database
in encrypted and thus sqlite can't effectively determine the pagesize. this causes an issue in
cases where bytes 16 & 17 of the page header are a power of 2 as reported by John Lehman
*/
ctx->page_sz = SQLITE_DEFAULT_PAGE_SIZE;
if((rc = cipher_ctx_init(&ctx->read_ctx)) != SQLITE_OK) return rc;
if((rc = cipher_ctx_init(&ctx->write_ctx)) != SQLITE_OK) return rc;
/* pre-allocate a page buffer of PageSize bytes. This will
be used as a persistent buffer for encryption and decryption
operations to avoid overhead of multiple memory allocations*/
ctx->buffer = sqlite3Malloc(SQLITE_DEFAULT_PAGE_SIZE);
if(ctx->buffer == NULL) return SQLITE_NOMEM;
/* allocate space for salt data. Then read the first 16 bytes
directly off the database file. This is the salt for the
key derivation function. If we get a short read allocate
@@ -460,19 +610,15 @@ int sqlite3CodecAttach(sqlite3* db, int nDb, const void *zKey, int nKey) {
codec_set_cipher_name(db, nDb, CIPHER, 0);
codec_set_kdf_iter(db, nDb, PBKDF2_ITER, 0);
codec_set_pass_key(db, nDb, zKey, nKey, 0);
/* Use HMAC signatures by default. Note that codec_set_use_hmac will implicity call
codec_set_page_size to set the default */
if((rc = codec_set_use_hmac(db, nDb, 1)) != SQLITE_OK) return rc;
cipher_ctx_copy(ctx->write_ctx, ctx->read_ctx);
sqlite3_mutex_enter(db->mutex);
/* Always overwrite page size and set to the default because the first page of the database
in encrypted and thus sqlite can't effectively determine the pagesize. this causes an issue in
cases where bytes 16 & 17 of the page header are a power of 2 as reported by John Lehman
Note: before forcing the page size we need to force pageSizeFixed to 0, else
sqliteBtreeSetPageSize will block the change
*/
pDb->pBt->pBt->pageSizeFixed = 0;
sqlite3BtreeSetPageSize(ctx->pBt, SQLITE_DEFAULT_PAGE_SIZE, EVP_MAX_IV_LENGTH, 0);
/* if fd is null, then this is an in-memory database and
we dont' want to overwrite the AutoVacuum settings
@@ -548,15 +694,6 @@ int sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey) {
sqlite3_mutex_enter(db->mutex);
if(ctx->read_ctx->iv_sz != ctx->write_ctx->iv_sz) {
char *error;
CODEC_TRACE(("sqlite3_rekey: updating page size for iv_sz change from %d to %d\n", ctx->read_ctx->iv_sz, ctx->write_ctx->iv_sz));
db->nextPagesize = SQLITE_DEFAULT_PAGE_SIZE;
pDb->pBt->pBt->pageSizeFixed = 0; /* required for sqlite3BtreeSetPageSize to modify pagesize setting */
sqlite3BtreeSetPageSize(pDb->pBt, db->nextPagesize, EVP_MAX_IV_LENGTH, 0);
sqlite3RunVacuum(&error, db);
}
codec_set_pass_key(db, 0, pKey, nKey, 1);
ctx->mode_rekey = 1;
@@ -584,7 +721,6 @@ int sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey) {
/* 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"));
db->nextPagesize = SQLITE_DEFAULT_PAGE_SIZE;
rc = sqlite3BtreeCommit(pDb->pBt);
cipher_ctx_copy(ctx->read_ctx, ctx->write_ctx);
} else {
+16 -4
View File
@@ -1480,20 +1480,32 @@ void sqlite3Pragma(
/** BEGIN CRYPTO **/
if( sqlite3StrICmp(zLeft, "cipher")==0 && zRight ){
extern int codec_set_cipher_name(sqlite3*, int, const char *, int);
codec_set_cipher_name(db,0,zRight,2); // change cipher for both
codec_set_cipher_name(db, iDb, zRight, 2); // change cipher for both
}else
if( sqlite3StrICmp(zLeft, "rekey_cipher")==0 && zRight ){
extern int codec_set_cipher_name(sqlite3*, int, const char *, int);
codec_set_cipher_name(db,0,zRight,1); // change write cipher only
codec_set_cipher_name(db, iDb, zRight, 1); // change write cipher only
}else
if( sqlite3StrICmp(zLeft, "kdf_iter")==0 && zRight ){
extern int codec_set_kdf_iter(sqlite3*, int, int, int);
codec_set_kdf_iter(db,0,atoi(zRight),2); // change cipher for both
codec_set_kdf_iter(db, iDb, atoi(zRight), 2); // change of RW PBKDF2 iteration
}else
if( sqlite3StrICmp(zLeft, "rekey_kdf_iter")==0 && zRight ){
extern int codec_set_kdf_iter(sqlite3*, int, int, int);
codec_set_kdf_iter(db,0,atoi(zRight),1); // change write cipher only
codec_set_kdf_iter(db, iDb, atoi(zRight), 1); // change # if W iterations
}else
if( sqlite3StrICmp(zLeft,"cipher_page_size")==0 ){
extern int codec_set_page_size(sqlite3*, int, int);
codec_set_page_size(db, iDb, atoi(zRight)); // change page size
}
if( sqlite3StrICmp(zLeft,"cipher_use_hmac")==0 ){
extern int codec_set_use_hmac(sqlite3*, int, int);
if(getBoolean(zRight)) {
codec_set_use_hmac(db, iDb, 1);
} else {
codec_set_use_hmac(db, iDb, 0);
}
}
/** END CRYPTO **/
#endif
#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD)
+132
View File
@@ -545,5 +545,137 @@ do_test unencryped-attach-unencrypted {
SELECT count(*) FROM t1;
}
} {1000}
db close
db2 close
file delete -force test.db
file delete -force test2.db
# 1. create a database with a custom page size,
# 2. create table and insert operations should work
# 3. close database, open it again with the same
# key and page size
# 4. verify that the table is readable
# and the data just inserted is visible
do_test custom-pagesize {
sqlite_orig db test.db
execsql {
PRAGMA key = 'testkey';
PRAGMA cipher_page_size = 4096;
CREATE table t1(a,b);
BEGIN;
}
for {set i 1} {$i<=1000} {incr i} {
set r [expr {int(rand()*500000)}]
execsql "INSERT INTO t1 VALUES($i,'value $r');"
}
execsql {
COMMIT;
}
db close
sqlite_orig db test.db
execsql {
PRAGMA key = 'testkey';
PRAGMA cipher_page_size = 4096;
SELECT count(*) FROM t1;
}
} {1000}
db close
# open the database with the default page size
## and verfiy that it is not readable
do_test custom-pagesize-must-match {
sqlite_orig db test.db
catchsql {
PRAGMA key = 'testkey';
SELECT name FROM sqlite_master WHERE type='table';
}
} {1 {file is encrypted or is not a database}}
db close
file delete -force test.db
# 1. create a database and insert a bunch of data, close the database
# 2. seek to the middle of a database page and write some junk
# 3. Open the database and verify that the database is no longer readable
do_test hmac-tamper-resistence {
sqlite_orig db test.db
execsql {
PRAGMA key = 'testkey';
CREATE table t1(a,b);
BEGIN;
}
for {set i 1} {$i<=1000} {incr i} {
set r [expr {int(rand()*500000)}]
execsql "INSERT INTO t1 VALUES($i,'value $r');"
}
execsql {
COMMIT;
}
db close
# write some junk into the middle of the page
hexio_write test.db 2560 00
sqlite_orig db test.db
catchsql {
PRAGMA key = 'testkey';
SELECT count(*) FROM t1;
}
} {1 {database disk image is malformed}}
db close
file delete -force test.db
# 1. create a database and insert a bunch of data, close the database
# 2. seek to the middle of a database page and write some junk
# 3. Open the database and verify that the database is still readable
do_test nohmac-not-tamper-resistent {
sqlite_orig db test.db
execsql {
PRAGMA key = 'testkey';
PRAGMA cipher_use_hmac = OFF;
PRAGMA cipher_page_size = 1024;
CREATE table t1(a,b);
BEGIN;
}
for {set i 1} {$i<=1000} {incr i} {
set r [expr {int(rand()*500000)}]
execsql "INSERT INTO t1 VALUES($i,'value $r');"
}
execsql {
COMMIT;
}
db close
# write some junk into the middle of the page
hexio_write test.db 2560 00
sqlite_orig db test.db
execsql {
PRAGMA key = 'testkey';
PRAGMA cipher_use_hmac = OFF;
PRAGMA cipher_page_size = 1024;
SELECT count(*) FROM t1;
}
} {1000}
db close
file delete -force test.db
finish_test