/* * ctx - codec context * pgno - page number in database * size - size in bytes of input and output buffers * mode - 1 to encrypt, 0 to decrypt * in - pointer to input bytes * out - pouter to output bytes */ static int codec_cipher(cipher_ctx ctx, Pgno pgno, int mode, int size, byte[] bIn, byte[] bOut) { int iv; int tmp_csz, csz; CODEC_TRACE("codec_cipher:entered pgno=%d, mode=%d, size=%d\n", pgno, mode, size); /* just copy raw data from in to out when key size is 0 * i.e. during a rekey of a plaintext database */ if (ctx.key_sz == 0) { Array.Copy(bIn, bOut, bIn.Length); //memcpy(out, in, size); return(SQLITE_OK); } MemoryStream dataStream = new MemoryStream(); CryptoStream encryptionStream; if (mode == CIPHER_ENCRYPT) { encryptionStream = new CryptoStream(dataStream, ctx.encryptor, CryptoStreamMode.Write); } else { encryptionStream = new CryptoStream(dataStream, ctx.decryptor, CryptoStreamMode.Write); } encryptionStream.Write(bIn, 0, size); encryptionStream.FlushFinalBlock(); dataStream.Position = 0; dataStream.Read(bOut, 0, (int)dataStream.Length); encryptionStream.Close(); dataStream.Close(); return(SQLITE_OK); }
/* sqlite3_rekey ** Given a database, this will reencrypt the database using a new key. ** There are two possible modes of operation. The first is rekeying ** an existing database that was not previously encrypted. The second ** is to change the key on an existing database. ** ** The proposed logic for this function follows: ** 1. Determine if there is already a key present ** 2. If there is NOT already a key present, create one and attach a codec (key would be null) ** 3. Initialize a ctx.rekey parameter of the codec ** ** Note: this will require modifications to the sqlite3Codec to support rekey ** */ static int sqlite3_rekey(sqlite3 db, string pKey, int nKey) { CODEC_TRACE("sqlite3_rekey: entered db=%d pKey=%s, nKey=%d\n", db, pKey, nKey); //activate_openssl(); if (db != null && pKey != null) { Db pDb = db.aDb[0]; CODEC_TRACE("sqlite3_rekey: database pDb=%d\n", pDb); if (pDb.pBt != null) { codec_ctx ctx = null; int rc; Pgno page_count = 0; Pgno pgno; PgHdr page = null; Pager pPager = pDb.pBt.pBt.pPager; sqlite3pager_get_codec(pDb.pBt.pBt.pPager, ref ctx); if (ctx == null) { CODEC_TRACE("sqlite3_rekey: no codec attached to db, attaching now\n"); /* there was no codec attached to this database,so attach one now with a null password */ sqlite3CodecAttach(db, 0, pKey, nKey); sqlite3pager_get_codec(pDb.pBt.pBt.pPager, ref ctx); /* prepare this setup as if it had already been initialized */ Buffer.BlockCopy(Encoding.UTF8.GetBytes(SQLITE_FILE_HEADER), 0, ctx.read_ctx.iv, 0, FILE_HEADER_SZ); ctx.read_ctx.key_sz = ctx.read_ctx.iv_sz = ctx.read_ctx.pass_sz = 0; } //if ( ctx.read_ctx.iv_sz != ctx.write_ctx.iv_sz ) //{ // string 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 = sqlite3BtreeGetPageSize( pDb.pBt ); // pDb.pBt.pBt.pageSizeFixed = false; /* required for sqlite3BtreeSetPageSize to modify pagesize setting */ // sqlite3BtreeSetPageSize( pDb.pBt, db.nextPagesize, MAX_IV_LENGTH, 0 ); // sqlite3RunVacuum( ref error, db ); //} codec_set_pass_key(db, 0, pKey, nKey, 1); ctx.mode_rekey = 1; /* do stuff here to rewrite the database ** 1. Create a transaction on the database ** 2. Iterate through each page, reading it and then writing it. ** 3. If that goes ok then commit and put ctx.rekey into ctx.key ** note: don't deallocate rekey since it may be used in a subsequent iteration */ rc = sqlite3BtreeBeginTrans(pDb.pBt, 1); /* begin write transaction */ sqlite3PagerPagecount(pPager, out page_count); for (pgno = 1; rc == SQLITE_OK && pgno <= page_count; pgno++) { /* pgno's start at 1 see pager.c:pagerAcquire */ if (0 == sqlite3pager_is_mj_pgno(pPager, pgno)) { /* skip this page (see pager.c:pagerAcquire for reasoning) */ rc = sqlite3PagerGet(pPager, pgno, ref page); if (rc == SQLITE_OK) { /* write page see pager_incr_changecounter for example */ rc = sqlite3PagerWrite(page); //printf("sqlite3PagerWrite(%d)\n", pgno); if (rc == SQLITE_OK) { sqlite3PagerUnref(page); } } } } /* 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 = sqlite3BtreeGetPageSize(pDb.pBt); rc = sqlite3BtreeCommit(pDb.pBt); if (ctx != null) { cipher_ctx_copy(ctx.read_ctx, ctx.write_ctx); } } else { CODEC_TRACE("sqlite3_rekey: rollback\n"); sqlite3BtreeRollback(pDb.pBt); } ctx.mode_rekey = 0; } return(SQLITE_OK); } return(SQLITE_ERROR); }
/* * sqlite3Codec can be called in multiple modes. * encrypt mode - expected to return a pointer to the * encrypted data without altering pData. * decrypt mode - expected to return a pointer to pData, with * the data decrypted in the input buffer */ static byte[] sqlite3Codec(codec_ctx iCtx, byte[] data, Pgno pgno, int mode) { codec_ctx ctx = (codec_ctx)iCtx; int pg_sz = sqlite3BtreeGetPageSize(ctx.pBt); int offset = 0; byte[] pData = data; CODEC_TRACE("sqlite3Codec: entered pgno=%d, mode=%d, ctx.mode_rekey=%d, pg_sz=%d\n", pgno, mode, ctx.mode_rekey, pg_sz); /* derive key on first use if necessary */ if (ctx.read_ctx.derive_key) { codec_key_derive(ctx, ctx.read_ctx); ctx.read_ctx.derive_key = false; } if (ctx.write_ctx.derive_key) { if (cipher_ctx_cmp(ctx.write_ctx, ctx.read_ctx) == 0) { cipher_ctx_copy(ctx.write_ctx, ctx.read_ctx); // the relevant parameters are the same, just copy read key } else { codec_key_derive(ctx, ctx.write_ctx); ctx.write_ctx.derive_key = false; } } CODEC_TRACE("sqlite3Codec: switch mode=%d offset=%d\n", mode, offset); if (ctx.buffer.Length != pg_sz) { ctx.buffer = sqlite3MemMalloc(pg_sz); } switch (mode) { case SQLITE_DECRYPT: codec_cipher(ctx.read_ctx, pgno, CIPHER_DECRYPT, pg_sz, pData, ctx.buffer); if (pgno == 1) { Buffer.BlockCopy(Encoding.UTF8.GetBytes(SQLITE_FILE_HEADER), 0, ctx.buffer, 0, FILE_HEADER_SZ); // memcpy( ctx.buffer, SQLITE_FILE_HEADER, FILE_HEADER_SZ ); /* copy file header to the first 16 bytes of the page */ } Buffer.BlockCopy(ctx.buffer, 0, pData, 0, pg_sz); //memcpy( pData, ctx.buffer, pg_sz ); /* copy buffer data back to pData and return */ return(pData); case SQLITE_ENCRYPT_WRITE_CTX: /* encrypt */ if (pgno == 1) { Buffer.BlockCopy(ctx.write_ctx.iv, 0, ctx.buffer, 0, FILE_HEADER_SZ); //memcpy( ctx.buffer, ctx.iv, FILE_HEADER_SZ ); /* copy salt to output buffer */ } codec_cipher(ctx.write_ctx, pgno, CIPHER_ENCRYPT, pg_sz, pData, ctx.buffer); return(ctx.buffer); /* return persistent buffer data, pData remains intact */ case SQLITE_ENCRYPT_READ_CTX: if (pgno == 1) { Buffer.BlockCopy(ctx.read_ctx.iv, 0, ctx.buffer, 0, FILE_HEADER_SZ); //memcpy( ctx.buffer, ctx.iv, FILE_HEADER_SZ ); /* copy salt to output buffer */ } codec_cipher(ctx.read_ctx, pgno, CIPHER_ENCRYPT, pg_sz, pData, ctx.buffer); return(ctx.buffer); /* return persistent buffer data, pData remains intact */ default: return(pData); } }
static int sqlite3pager_is_mj_pgno(Pager pPager, Pgno pgno) { return((PAGER_MJ_PGNO(pPager) == pgno) ? 1 : 0); }