2014-01-29 19:09:30 -08:00
using System ;
using System.Text ;
using Pgno = System . UInt32 ;
2015-04-08 15:20:10 -07:00
using u16 = System . UInt16 ;
2014-01-29 19:09:30 -08:00
namespace Community.CsharpSqlite
{
2015-04-08 15:20:10 -07:00
#if ! SQLITE_WINRT
using System.IO ;
using System.Security.Cryptography ;
2014-01-29 19:09:30 -08:00
#endif
2015-04-08 15:20:10 -07:00
public partial class Sqlite3
{
/ *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * Included in SQLite3 port to C # - SQLite ; 2010 Noah B Hart , Diego Torres
* * C # - SQLite is an independent reimplementation of the SQLite software library
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* /
/ *
* * SQLCipher
* * crypto . c developed by Stephen Lombardo ( Zetetic LLC )
* * sjlombardo at zetetic dot net
* * http : //zetetic.net
* *
* * Copyright ( c ) 2009 , ZETETIC LLC
* * All rights reserved .
* *
* * Redistribution and use in source and binary forms , with or without
* * modification , are permitted provided that the following conditions are met :
* * * Redistributions of source code must retain the above copyright
* * notice , this list of conditions and the following disclaimer .
* * * Redistributions in binary form must reproduce the above copyright
* * notice , this list of conditions and the following disclaimer in the
* * documentation and / or other materials provided with the distribution .
* * * Neither the name of the ZETETIC LLC nor the
* * names of its contributors may be used to endorse or promote products
* * derived from this software without specific prior written permission .
* *
* * THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ' ' AS IS ' ' AND ANY
* * EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED
* * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* * DISCLAIMED . IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY
* * DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES
* * ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ;
* * LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND
* * ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF THIS
* * SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
* *
* /
/* BEGIN CRYPTO */
2014-01-29 19:09:30 -08:00
#if SQLITE_HAS_CODEC
2015-04-08 15:20:10 -07:00
//#include <assert.h>
//#include <openssl/evp.h>
//#include <openssl/rand.h>
//#include <openssl/hmac.h>
//#include "sqliteInt.h"
//#include "btreeInt.h"
//#include "crypto.h"
2014-01-29 19:09:30 -08:00
#if CODEC_DEBUG | | TRACE
//#define CODEC_TRACE(X) {printf X;fflush(stdout);}
static void CODEC_TRACE ( string T , params object [ ] ap ) { if ( sqlite3PagerTrace ) sqlite3DebugPrintf ( T , ap ) ; }
#else
2015-04-08 15:20:10 -07:00
//#define CODEC_TRACE(X)
private static void CODEC_TRACE ( string T , params object [ ] ap )
{
}
2014-01-29 19:09:30 -08:00
#endif
2015-04-08 15:20:10 -07:00
//void sqlite3FreeCodecArg(void *pCodecArg);
public class cipher_ctx
{ //typedef struct {
public string pass ;
public int pass_sz ;
public bool derive_key ;
public byte [ ] key ;
public int key_sz ;
public byte [ ] iv ;
public int iv_sz ;
public ICryptoTransform encryptor ;
public ICryptoTransform decryptor ;
public cipher_ctx Copy ( )
{
cipher_ctx c = new cipher_ctx ( ) ;
c . derive_key = derive_key ;
c . pass = pass ;
c . pass_sz = pass_sz ;
if ( key ! = null )
{
c . key = new byte [ key . Length ] ;
key . CopyTo ( c . key , 0 ) ;
}
c . key_sz = key_sz ;
if ( iv ! = null )
{
c . iv = new byte [ iv . Length ] ;
iv . CopyTo ( c . iv , 0 ) ;
}
c . iv_sz = iv_sz ;
c . encryptor = encryptor ;
c . decryptor = decryptor ;
return c ;
}
public void CopyTo ( cipher_ctx ct )
{
ct . derive_key = derive_key ;
ct . pass = pass ;
ct . pass_sz = pass_sz ;
if ( key ! = null )
{
ct . key = new byte [ key . Length ] ;
key . CopyTo ( ct . key , 0 ) ;
}
ct . key_sz = key_sz ;
if ( iv ! = null )
{
ct . iv = new byte [ iv . Length ] ;
iv . CopyTo ( ct . iv , 0 ) ;
}
ct . iv_sz = iv_sz ;
ct . encryptor = encryptor ;
ct . decryptor = decryptor ;
}
}
public class codec_ctx
{ //typedef struct {
public int mode_rekey ;
public byte [ ] buffer ;
public Btree pBt ;
public cipher_ctx read_ctx ;
public cipher_ctx write_ctx ;
public codec_ctx Copy ( )
{
codec_ctx c = new codec_ctx ( ) ;
c . mode_rekey = mode_rekey ;
c . buffer = sqlite3MemMalloc ( buffer . Length ) ;
c . pBt = pBt ;
if ( read_ctx ! = null )
c . read_ctx = read_ctx . Copy ( ) ;
if ( write_ctx ! = null )
c . write_ctx = write_ctx . Copy ( ) ;
return c ;
}
}
private const int FILE_HEADER_SZ = 16 ; //#define FILE_HEADER_SZ 16
private const string CIPHER = "aes-256-cbc" ; //#define CIPHER "aes-256-cbc"
private const int CIPHER_DECRYPT = 0 ; //#define CIPHER_DECRYPT 0
private const int CIPHER_ENCRYPT = 1 ; //#define CIPHER_ENCRYPT 1
2014-01-29 19:09:30 -08:00
#if NET_2_0
static RijndaelManaged Aes = new RijndaelManaged ( ) ;
#else
2015-04-08 15:20:10 -07:00
private static AesManaged Aes = new AesManaged ( ) ;
2014-01-29 19:09:30 -08:00
#endif
2015-04-08 15:20:10 -07:00
/* BEGIN CRYPTO */
private static void sqlite3pager_get_codec ( Pager pPager , ref codec_ctx ctx )
{
ctx = pPager . pCodec ;
}
private static int sqlite3pager_is_mj_pgno ( Pager pPager , Pgno pgno )
{
return ( PAGER_MJ_PGNO ( pPager ) = = pgno ) ? 1 : 0 ;
}
private static sqlite3_file sqlite3Pager_get_fd ( Pager pPager )
{
return ( isOpen ( pPager . fd ) ) ? pPager . fd : null ;
}
private static void sqlite3pager_sqlite3PagerSetCodec (
Pager pPager ,
dxCodec xCodec ,
dxCodecSizeChng xCodecSizeChng ,
dxCodecFree xCodecFree ,
codec_ctx pCodec
)
{
sqlite3PagerSetCodec ( pPager , xCodec , xCodecSizeChng , xCodecFree , pCodec ) ;
}
/* END CRYPTO */
//static void activate_openssl() {
// if(EVP_get_cipherbyname(CIPHER) == null) {
// OpenSSL_add_all_algorithms();
// }
//}
/ * *
* Free and wipe memory
* If ptr is not null memory will be freed .
* If sz is greater than zero , the memory will be overwritten with zero before it is freed
* /
private static void codec_free ( ref byte [ ] ptr , int sz )
{
if ( ptr ! = null )
{
if ( sz > 0 )
Array . Clear ( ptr , 0 , sz ) ; //memset( ptr, 0, sz );
sqlite3_free ( ref ptr ) ;
}
}
/ * *
* Set the raw password / key data for a cipher context
*
* returns SQLITE_OK if assignment was successfull
* returns SQLITE_NOMEM if an error occured allocating memory
* returns SQLITE_ERROR if the key couldn ' t be set because the pass was null or size was zero
* /
private static int cipher_ctx_set_pass ( cipher_ctx ctx , string zKey , int nKey )
{
ctx . pass = null ; // codec_free( ctx.pass, ctx.pass_sz );
ctx . pass_sz = nKey ;
if ( ! String . IsNullOrEmpty ( zKey ) & & nKey > 0 )
{
//ctx.pass = sqlite3Malloc(nKey);
//if(ctx.pass == null) return SQLITE_NOMEM;
ctx . pass = zKey ; //memcpy(ctx.pass, zKey, nKey);
return SQLITE_OK ;
}
return SQLITE_ERROR ;
}
/ * *
* Initialize a new cipher_ctx struct . This function will allocate memory
* for the cipher context and for the key
*
* returns SQLITE_OK if initialization was successful
* returns SQLITE_NOMEM if an error occured allocating memory
* /
private static int cipher_ctx_init ( ref cipher_ctx iCtx )
{
iCtx = new cipher_ctx ( ) ;
//iCtx = sqlite3Malloc( sizeof( cipher_ctx ) );
//ctx = *iCtx;
//if ( ctx == null ) return SQLITE_NOMEM;
//memset( ctx, 0, sizeof( cipher_ctx ) );
//ctx.key = sqlite3Malloc( EVP_MAX_KEY_LENGTH );
//if ( ctx.key == null ) return SQLITE_NOMEM;
return SQLITE_OK ;
}
/ * *
* free and wipe memory associated with a cipher_ctx
* /
private static void cipher_ctx_free ( ref cipher_ctx ictx )
{
cipher_ctx ctx = ictx ;
CODEC_TRACE ( "cipher_ctx_free: entered ictx=%d\n" , ictx ) ;
ctx . pass = null ; //codec_free(ctx.pass, ctx.pass_sz);
if ( ctx . key ! = null )
Array . Clear ( ctx . key , 0 , ctx . key . Length ) ; //codec_free(ctx.key, ctx.key_sz);
if ( ctx . iv ! = null )
Array . Clear ( ctx . iv , 0 , ctx . iv . Length ) ;
ictx = new cipher_ctx ( ) ; // codec_free( ref ctx, sizeof( cipher_ctx ) );
}
/ * *
* Copy one cipher_ctx to another . For instance , assuming that read_ctx is a
* fully initialized context , you could copy it to write_ctx and all yet data
* and pass information across
*
* returns SQLITE_OK if initialization was successful
* returns SQLITE_NOMEM if an error occured allocating memory
* /
private static int cipher_ctx_copy ( cipher_ctx target , cipher_ctx source )
{
//byte[] key = target.key;
CODEC_TRACE ( "cipher_ctx_copy: entered target=%d, source=%d\n" , target , source ) ;
//codec_free(target.pass, target.pass_sz);
source . CopyTo ( target ) ; //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.pass = sqlite3Malloc(source.pass_sz);
//if(target.pass == null) return SQLITE_NOMEM;
//memcpy(target.pass, source.pass, source.pass_sz);
return SQLITE_OK ;
}
/ * *
* Compare one cipher_ctx to another .
*
* returns 0 if all the parameters ( except the derived key data ) are the same
* returns 1 otherwise
* /
private static int cipher_ctx_cmp ( cipher_ctx c1 , cipher_ctx c2 )
{
CODEC_TRACE ( "cipher_ctx_cmp: entered c1=%d c2=%d\n" , c1 , c2 ) ;
if ( c1 . key_sz = = c2 . key_sz
& & c1 . pass_sz = = c2 . pass_sz
& & c1 . pass = = c2 . pass
)
return 0 ;
return 1 ;
}
/ * *
* Free and wipe memory associated with a cipher_ctx , including the allocated
* read_ctx and write_ctx .
* /
private static void codec_ctx_free ( ref codec_ctx iCtx )
{
codec_ctx ctx = iCtx ;
CODEC_TRACE ( "codec_ctx_free: entered iCtx=%d\n" , iCtx ) ;
cipher_ctx_free ( ref ctx . read_ctx ) ;
cipher_ctx_free ( ref ctx . write_ctx ) ;
iCtx = new codec_ctx ( ) ; //codec_free(ctx, sizeof(codec_ctx);
}
/ * *
* Derive an encryption key for a cipher contex key based on the raw password .
*
* If the raw key data is formated as x ' hex ' and there are exactly enough hex chars to fill
* the key space ( i . e 64 hex chars for a 256 bit key ) then the key data will be used directly .
*
* 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 )
* /
private 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.iv=%d ctx.iv_sz=%d c_ctx.kdf_iter=%d c_ctx.key_sz=%d\n" ,
c_ctx . pass , c_ctx . pass_sz , c_ctx . iv , c_ctx . iv_sz , c_ctx . key_sz ) ;
if ( c_ctx . pass ! = null & & c_ctx . pass_sz > 0 )
{ // if pass is not null
if ( ( c_ctx . pass_sz = = ( c_ctx . key_sz * 2 ) + 3 ) & & c_ctx . pass . StartsWith ( "x'" , StringComparison . InvariantCultureIgnoreCase ) )
{
int n = c_ctx . pass_sz - 3 ; /* adjust for leading x' and tailing ' */
string z = c_ctx . pass . Substring ( 2 ) ; // + 2; /* adjust lead offset of x' */
CODEC_TRACE ( "codec_key_derive: deriving key from hex\n" ) ;
c_ctx . key = sqlite3HexToBlob ( null , z , n ) ;
}
else
{
CODEC_TRACE ( "codec_key_derive: deriving key using AES256\n" ) ;
Rfc2898DeriveBytes k1 = new Rfc2898DeriveBytes ( c_ctx . pass , c_ctx . iv , 2010 ) ;
c_ctx . key_sz = 32 ;
c_ctx . key = k1 . GetBytes ( c_ctx . key_sz ) ;
}
2014-01-29 19:09:30 -08:00
#if NET_2_0
Aes . BlockSize = 0x80 ;
Aes . FeedbackSize = 8 ;
Aes . KeySize = 0x100 ;
Aes . Mode = CipherMode . CBC ;
#endif
2015-04-08 15:20:10 -07:00
c_ctx . encryptor = Aes . CreateEncryptor ( c_ctx . key , c_ctx . iv ) ;
c_ctx . decryptor = Aes . CreateDecryptor ( c_ctx . key , c_ctx . iv ) ;
return SQLITE_OK ;
} ;
return SQLITE_ERROR ;
}
/ *
* 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
* /
private 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 ;
}
/ * *
*
* when for_ctx = = 0 then it will change for read
* when for_ctx = = 1 then it will change for write
* when for_ctx = = 2 then it will change for both
* /
private static int codec_set_cipher_name ( sqlite3 db , int nDb , string cipher_name , int for_ctx )
{
Db pDb = db . aDb [ nDb ] ;
CODEC_TRACE ( "codec_set_cipher_name: entered db=%d nDb=%d cipher_name=%s for_ctx=%d\n" , db , nDb , cipher_name , for_ctx ) ;
if ( pDb . pBt ! = null )
{
codec_ctx ctx = null ;
cipher_ctx c_ctx ;
sqlite3pager_get_codec ( pDb . pBt . pBt . pPager , ref ctx ) ;
c_ctx = for_ctx ! = 0 ? ctx . write_ctx : ctx . read_ctx ;
c_ctx . derive_key = true ;
if ( for_ctx = = 2 )
cipher_ctx_copy ( for_ctx ! = 0 ? ctx . read_ctx : ctx . write_ctx , c_ctx ) ;
return SQLITE_OK ;
}
return SQLITE_ERROR ;
}
private static int codec_set_pass_key ( sqlite3 db , int nDb , string zKey , int nKey , int for_ctx )
{
Db pDb = db . aDb [ nDb ] ;
CODEC_TRACE ( "codec_set_pass_key: entered db=%d nDb=%d cipher_name=%s nKey=%d for_ctx=%d\n" , db , nDb , zKey , nKey , for_ctx ) ;
if ( pDb . pBt ! = null )
{
codec_ctx ctx = null ;
cipher_ctx c_ctx ;
sqlite3pager_get_codec ( pDb . pBt . pBt . pPager , ref ctx ) ;
c_ctx = for_ctx ! = 0 ? ctx . write_ctx : ctx . read_ctx ;
cipher_ctx_set_pass ( c_ctx , zKey , nKey ) ;
c_ctx . derive_key = true ;
if ( for_ctx = = 2 )
cipher_ctx_copy ( for_ctx ! = 0 ? ctx . read_ctx : ctx . write_ctx , c_ctx ) ;
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
* /
private 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 ;
}
}
private static int sqlite3CodecAttach ( sqlite3 db , int nDb , string zKey , int nKey )
{
Db pDb = db . aDb [ nDb ] ;
CODEC_TRACE ( "sqlite3CodecAttach: entered nDb=%d zKey=%s, nKey=%d\n" , nDb , zKey , nKey ) ;
//activate_openssl();
if ( zKey ! = null & & pDb . pBt ! = null )
{
Aes . KeySize = 256 ;
2014-01-29 19:09:30 -08:00
#if ! SQLITE_SILVERLIGHT
2015-04-08 15:20:10 -07:00
Aes . Padding = PaddingMode . None ;
2014-01-29 19:09:30 -08:00
#endif
2015-04-08 15:20:10 -07:00
codec_ctx ctx ;
int rc ;
////Pager pPager = pDb.pBt.pBt.pPager;
sqlite3_file fd ;
ctx = new codec_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 */
if ( ( rc = cipher_ctx_init ( ref ctx . read_ctx ) ) ! = SQLITE_OK )
return rc ;
if ( ( rc = cipher_ctx_init ( ref 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 = sqlite3MemMalloc ( sqlite3BtreeGetPageSize ( ctx . pBt ) ) ; //sqlite3Malloc(sqlite3BtreeGetPageSize(ctx.pBt);
//if(ctx.buffer == null) return SQLITE_NOMEM;
/* allocate space for salt data. Then read the first 16 bytes header as the salt for the key derivation */
ctx . read_ctx . iv_sz = FILE_HEADER_SZ ;
ctx . read_ctx . iv = new byte [ ctx . read_ctx . iv_sz ] ; //sqlite3Malloc( ctx.iv_sz );
Buffer . BlockCopy ( Encoding . UTF8 . GetBytes ( SQLITE_FILE_HEADER ) , 0 , ctx . read_ctx . iv , 0 , FILE_HEADER_SZ ) ;
sqlite3pager_sqlite3PagerSetCodec ( sqlite3BtreePager ( pDb . pBt ) , sqlite3Codec , null , sqlite3FreeCodecArg , ctx ) ;
codec_set_cipher_name ( db , nDb , CIPHER , 0 ) ;
codec_set_pass_key ( db , nDb , zKey , nKey , 0 ) ;
cipher_ctx_copy ( ctx . write_ctx , ctx . read_ctx ) ;
//sqlite3BtreeSetPageSize( ctx.pBt, sqlite3BtreeGetPageSize( ctx.pBt ), MAX_IV_LENGTH, 0 );
}
return SQLITE_OK ;
}
private static void sqlite3FreeCodecArg ( ref codec_ctx pCodecArg )
{
if ( pCodecArg = = null )
return ;
codec_ctx_free ( ref pCodecArg ) ; // wipe and free allocated memory for the context
}
private static void sqlite3_activate_see ( string zPassword )
{
/* do nothing, security enhancements are always active */
}
static public int sqlite3_key ( sqlite3 db , string pKey , int nKey )
{
CODEC_TRACE ( "sqlite3_key: entered db=%d pKey=%s nKey=%d\n" , db , pKey , nKey ) ;
/* attach key if db and pKey are not null and nKey is > 0 */
if ( db ! = null & & pKey ! = null )
{
sqlite3CodecAttach ( db , 0 , pKey , nKey ) ; // operate only on the main db
//
// If we are reopening an existing database, redo the header information setup
//
BtShared pBt = db . aDb [ 0 ] . pBt . pBt ;
byte [ ] zDbHeader = sqlite3MemMalloc ( ( int ) pBt . pageSize ) ; // pBt.pPager.pCodec.buffer;
sqlite3PagerReadFileheader ( pBt . pPager , zDbHeader . Length , zDbHeader ) ;
if ( sqlite3Get4byte ( zDbHeader ) > 0 ) // Existing Database, need to reset some values
{
CODEC2 ( pBt . pPager , zDbHeader , 2 , SQLITE_DECRYPT , ref zDbHeader ) ;
byte nReserve = zDbHeader [ 20 ] ;
pBt . pageSize = ( uint ) ( ( zDbHeader [ 16 ] < < 8 ) | ( zDbHeader [ 17 ] < < 16 ) ) ;
if ( pBt . pageSize < 512 | | pBt . pageSize > SQLITE_MAX_PAGE_SIZE
| | ( ( pBt . pageSize - 1 ) & pBt . pageSize ) ! = 0 )
pBt . pageSize = 0 ;
pBt . pageSizeFixed = true ;
2014-01-29 19:09:30 -08:00
#if ! SQLITE_OMIT_AUTOVACUUM
2015-04-08 15:20:10 -07:00
pBt . autoVacuum = sqlite3Get4byte ( zDbHeader , 36 + 4 * 4 ) ! = 0 ;
pBt . incrVacuum = sqlite3Get4byte ( zDbHeader , 36 + 7 * 4 ) ! = 0 ;
2014-01-29 19:09:30 -08:00
#endif
2015-04-08 15:20:10 -07:00
sqlite3PagerSetPagesize ( pBt . pPager , ref pBt . pageSize , nReserve ) ;
pBt . usableSize = ( u16 ) ( pBt . pageSize - nReserve ) ;
}
return SQLITE_OK ;
}
return SQLITE_ERROR ;
}
/ * 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
* *
* /
public 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 ;
}
private static void sqlite3CodecGetKey ( sqlite3 db , int nDb , out string zKey , out int nKey )
{
Db pDb = db . aDb [ nDb ] ;
CODEC_TRACE ( "sqlite3CodecGetKey: entered db=%d, nDb=%d\n" , db , nDb ) ;
if ( pDb . pBt ! = null )
{
codec_ctx ctx = null ;
sqlite3pager_get_codec ( pDb . pBt . pBt . pPager , ref ctx ) ;
if ( ctx ! = null )
{ /* if the codec has an attached codec_context user the raw key data */
zKey = ctx . read_ctx . pass ;
nKey = ctx . read_ctx . pass_sz ;
return ;
}
}
zKey = null ;
nKey = 0 ;
}
/* END CRYPTO */
2014-01-29 19:09:30 -08:00
#endif
2015-04-08 15:20:10 -07:00
private const int SQLITE_ENCRYPT_WRITE_CTX = 6 ; /* Encode page */
private const int SQLITE_ENCRYPT_READ_CTX = 7 ; /* Encode page */
private const int SQLITE_DECRYPT = 3 ; /* Decode page */
}
}