pragma allows le, be, or native pgno ordering plus tests

This commit is contained in:
Stephen Lombardo 2012-07-18 11:21:12 -04:00
parent 7f2b44c8e2
commit f1ea44e3ef
6 changed files with 73 additions and 15 deletions

BIN
sqlcipher-2.0-be-testkey.db Normal file

Binary file not shown.

View File

@ -129,6 +129,19 @@ int codec_pragma(sqlite3* db, int iDb, Parse *pParse, const char *zLeft, const c
rc = codec_set_btree_to_codec_pagesize(db, pDb, ctx);
if(rc != SQLITE_OK) sqlcipher_codec_ctx_set_error(ctx, rc);
}
}else
if( sqlite3StrICmp(zLeft,"cipher_hmac_pgno")==0 ){
// clear both pgno endian flags
if(sqlite3StrICmp(zRight, "le") == 0) {
sqlcipher_codec_ctx_unset_flag(ctx, CIPHER_FLAG_BE_PGNO);
sqlcipher_codec_ctx_set_flag(ctx, CIPHER_FLAG_LE_PGNO);
} else if(sqlite3StrICmp(zRight, "be") == 0) {
sqlcipher_codec_ctx_unset_flag(ctx, CIPHER_FLAG_LE_PGNO);
sqlcipher_codec_ctx_set_flag(ctx, CIPHER_FLAG_BE_PGNO);
} else if(sqlite3StrICmp(zRight, "native") == 0) {
sqlcipher_codec_ctx_unset_flag(ctx, CIPHER_FLAG_LE_PGNO);
sqlcipher_codec_ctx_unset_flag(ctx, CIPHER_FLAG_BE_PGNO);
}
}else {
return 0;
}

View File

@ -59,6 +59,7 @@
/* possible flags for cipher_ctx->flags */
#define CIPHER_FLAG_HMAC 0x01
#define CIPHER_FLAG_LE_PGNO 0x02
#define CIPHER_FLAG_BE_PGNO 0x04
#ifndef DEFAULT_CIPHER_FLAGS
#define DEFAULT_CIPHER_FLAGS CIPHER_FLAG_HMAC | CIPHER_FLAG_LE_PGNO

View File

@ -497,6 +497,7 @@ void sqlcipher_codec_ctx_free(codec_ctx **iCtx) {
sqlcipher_free(ctx, sizeof(codec_ctx));
}
/** convert a 32bit unsigned integer to little endian byte ordering */
static inline void sqlcipher_put4byte_le(unsigned char *p, u32 v) {
p[0] = (u8)v;
p[1] = (u8)(v>>8);
@ -505,29 +506,31 @@ static inline void sqlcipher_put4byte_le(unsigned char *p, u32 v) {
}
int sqlcipher_page_hmac(cipher_ctx *ctx, Pgno pgno, unsigned char *in, int in_sz, unsigned char *out) {
unsigned char pgno_le[4];
/* convert page number to consistent representation before calculating MAC for
unsigned char pgno_raw[sizeof(pgno)];
/* we may convert page number to consistent representation before calculating MAC for
compatibility across big-endian and little-endian platforms.
Note: The public release of sqlcipher 2.0.0 to 2.0.6 had a bug where the bytes of pgno
were used directly in the MAC. So, we convert to little endian instead of big endian, to
preserve backwards compatibility on the most popular platform */
sqlcipher_put4byte_le(pgno_le, pgno);
were used directly in the MAC. SQLCipher convert's to little endian by default to preserve
backwards compatibility on the most popular platforms, but can optionally be configured
to use either big endian or native byte ordering via pragma. */
if(ctx->flags & CIPHER_FLAG_LE_PGNO) { /* compute hmac using little endian pgno*/
sqlcipher_put4byte_le(pgno_raw, pgno);
} else if(ctx->flags & CIPHER_FLAG_BE_PGNO) { /* compute hmac using big endian pgno */
sqlite3Put4byte(pgno_raw, pgno); /* sqlite3Put4byte converts 32bit uint to big endian */
} else { /* use native byte ordering */
memcpy(pgno_raw, &pgno, sizeof(pgno));
}
HMAC_CTX_init(&ctx->hctx);
HMAC_Init_ex(&ctx->hctx, ctx->hmac_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(&ctx->hctx, in, in_sz);
if(ctx->flags & CIPHER_FLAG_LE_PGNO) /* default compute hmac using little endian */
HMAC_Update(&ctx->hctx, (const unsigned char*) pgno_le, sizeof(pgno_le));
else /* legacy setting - compute using native byte ordering */
HMAC_Update(&ctx->hctx, (const unsigned char*) &pgno, sizeof(pgno));
HMAC_Update(&ctx->hctx, (const unsigned char*) pgno_raw, sizeof(pgno));
HMAC_Final(&ctx->hctx, out, NULL);
HMAC_CTX_cleanup(&ctx->hctx);
return SQLITE_OK;

View File

@ -1556,9 +1556,10 @@ do_test multipage-schema-autovacuum-shortread-wal {
db close
file delete -force test.db
# open a 2.0 database verify it can be opened
do_test open-2.0-database {
sqlite_orig db sqlcipher-2.0-testkey.db
# open a 2.0 database with little endian hmac page numbers (default)
# verify it can be opened
do_test open-2.0-le-database {
sqlite_orig db sqlcipher-2.0-le-testkey.db
execsql {
PRAGMA key = 'testkey';
SELECT count(*) FROM t1;
@ -1567,4 +1568,44 @@ do_test open-2.0-database {
} {4 1 1 one one 1 2 one two}
db close
# open a 2.0 database with big-endian hmac page numbers
# verify it can be opened
do_test open-2.0-be-database {
sqlite_orig db sqlcipher-2.0-be-testkey.db
execsql {
PRAGMA key = 'testkey';
PRAGMA cipher_hmac_pgno = be;
SELECT count(*) FROM t1;
SELECT * FROM t1;
}
} {4 1 1 one one 1 2 one two}
db close
# open a 2.0 database with big-endian hmac page numbers
# attach a new database with little endian page numbers (default)
# copy schema between the two, and verify the latter
# can be opened
do_test be-to-le-migration {
sqlite_orig db sqlcipher-2.0-be-testkey.db
execsql {
PRAGMA key = 'testkey';
PRAGMA cipher_hmac_pgno = be;
ATTACH DATABASE 'test.db' AS db2 KEY 'testkey';
CREATE TABLE db2.t1(a,b);
INSERT INTO db2.t1 SELECT * FROM main.t1;
DETACH DATABASE db2;
}
db close
sqlite_orig db test.db
execsql {
PRAGMA key = 'testkey';
SELECT count(*) FROM t1;
SELECT * FROM t1;
}
} {4 1 1 one one 1 2 one two}
db close
file delete -force test.db
finish_test