aboutsummaryrefslogtreecommitdiffstats
path: root/Plugins/DbSqliteWx/codec.c
diff options
context:
space:
mode:
Diffstat (limited to 'Plugins/DbSqliteWx/codec.c')
-rw-r--r--Plugins/DbSqliteWx/codec.c3100
1 files changed, 2329 insertions, 771 deletions
diff --git a/Plugins/DbSqliteWx/codec.c b/Plugins/DbSqliteWx/codec.c
index 9a8ccd6..903c6b7 100644
--- a/Plugins/DbSqliteWx/codec.c
+++ b/Plugins/DbSqliteWx/codec.c
@@ -1,771 +1,2329 @@
-/*
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-// Name: codec.cpp
-// Purpose:
-// Author: Ulrich Telle
-// Modified by:
-// Created: 2006-12-06
-// RCS-ID: $$
-// Copyright: (c) Ulrich Telle
-// Licence: wxWindows licence + RSA Data Security license
-///////////////////////////////////////////////////////////////////////////////
-
-/// \file codec.cpp Implementation of MD5, RC4 and AES algorithms
-*/
-/*
- **********************************************************************
- ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **
- ** **
- ** License to copy and use this software is granted provided that **
- ** it is identified as the "RSA Data Security, Inc. MD5 Message **
- ** Digest Algorithm" in all material mentioning or referencing this **
- ** software or this function. **
- ** **
- ** License is also granted to make and use derivative works **
- ** provided that such works are identified as "derived from the RSA **
- ** Data Security, Inc. MD5 Message Digest Algorithm" in all **
- ** material mentioning or referencing the derived work. **
- ** **
- ** RSA Data Security, Inc. makes no representations concerning **
- ** either the merchantability of this software or the suitability **
- ** of this software for any particular purpose. It is provided "as **
- ** is" without express or implied warranty of any kind. **
- ** **
- ** These notices must be retained in any copies of any part of this **
- ** documentation and/or software. **
- **********************************************************************
- */
-
-#include "codec.h"
-
-#ifndef SQLITE_USER_AUTHENTICATION
-#if CODEC_TYPE == CODEC_TYPE_AES256
-#include "sha2.h"
-#include "sha2.c"
-#endif
-#endif
-
-/*
-// ----------------
-// MD5 by RSA
-// ----------------
-
-// C headers for MD5
-*/
-#include <sys/types.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#define MD5_HASHBYTES 16
-
-/*
-/// Structure representing an MD5 context while ecrypting. (For internal use only)
-*/
-typedef struct MD5Context
-{
- unsigned int buf[4];
- unsigned int bits[2];
- unsigned char in[64];
-} MD5_CTX;
-
-static void MD5Init(MD5_CTX *context);
-static void MD5Update(MD5_CTX *context, unsigned char *buf, unsigned len);
-static void MD5Final(unsigned char digest[MD5_HASHBYTES], MD5_CTX *context);
-static void MD5Transform(unsigned int buf[4], unsigned int in[16]);
-
-static void byteReverse(unsigned char *buf, unsigned longs);
-
-/*
- * Note: this code is harmless on little-endian machines.
- */
-static void byteReverse(unsigned char *buf, unsigned longs)
-{
- static int littleEndian = -1;
- if (littleEndian < 0)
- {
- /* Are we little or big endian? This method is from Harbison & Steele. */
- union
- {
- long l;
- char c[sizeof(long)];
- } u;
- u.l = 1;
- littleEndian = (u.c[0] == 1) ? 1 : 0;
- }
-
- if (littleEndian != 1)
- {
- unsigned int t;
- do
- {
- t = (unsigned int) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
- ((unsigned) buf[1] << 8 | buf[0]);
- *(unsigned int *) buf = t;
- buf += 4;
- }
- while (--longs);
- }
-}
-
-#if 0
-static char* MD5End(MD5_CTX *, char *);
-
-static char* MD5End(MD5_CTX *ctx, char *buf)
-{
- int i;
- unsigned char digest[MD5_HASHBYTES];
- char hex[]="0123456789abcdef";
-
- if (!buf)
- {
- buf = (char *)malloc(33);
- }
-
- if (!buf)
- {
- return 0;
- }
-
- MD5Final(digest,ctx);
- for (i=0;i<MD5_HASHBYTES;i++)
- {
- buf[i+i] = hex[digest[i] >> 4];
- buf[i+i+1] = hex[digest[i] & 0x0f];
- }
- buf[i+i] = '\0';
- return buf;
-}
-#endif
-
-/*
- * Final wrapup - pad to 64-byte boundary with the bit pattern
- * 1 0* (64-bit count of bits processed, MSB-first)
- */
-static void MD5Final(unsigned char digest[16], MD5_CTX *ctx)
-{
- unsigned count;
- unsigned char *p;
-
- /* Compute number of bytes mod 64 */
- count = (ctx->bits[0] >> 3) & 0x3F;
-
- /* Set the first char of padding to 0x80. This is safe since there is
- always at least one byte free */
- p = ctx->in + count;
- *p++ = 0x80;
-
- /* Bytes of padding needed to make 64 bytes */
- count = 64 - 1 - count;
-
- /* Pad out to 56 mod 64 */
- if (count < 8)
- {
- /* Two lots of padding: Pad the first block to 64 bytes */
- memset(p, 0, count);
- byteReverse(ctx->in, 16);
- MD5Transform(ctx->buf, (unsigned int *) ctx->in);
-
- /* Now fill the next block with 56 bytes */
- memset(ctx->in, 0, 56);
- }
- else
- {
- /* Pad block to 56 bytes */
- memset(p, 0, count - 8);
- }
- byteReverse(ctx->in, 14);
-
- /* Append length in bits and transform */
- ((unsigned int *) ctx->in)[14] = ctx->bits[0];
- ((unsigned int *) ctx->in)[15] = ctx->bits[1];
-
- MD5Transform(ctx->buf, (unsigned int *) ctx->in);
- byteReverse((unsigned char *) ctx->buf, 4);
- memcpy(digest, ctx->buf, 16);
- memset((char *) ctx, 0, sizeof(ctx)); /* In case it's sensitive */
-}
-
-static void MD5Init(MD5_CTX *ctx)
-{
- ctx->buf[0] = 0x67452301;
- ctx->buf[1] = 0xefcdab89;
- ctx->buf[2] = 0x98badcfe;
- ctx->buf[3] = 0x10325476;
-
- ctx->bits[0] = 0;
- ctx->bits[1] = 0;
-}
-
-static void MD5Update(MD5_CTX *ctx, unsigned char *buf, unsigned len)
-{
- unsigned int t;
-
- /* Update bitcount */
-
- t = ctx->bits[0];
- if ((ctx->bits[0] = t + ((unsigned int) len << 3)) < t)
- {
- ctx->bits[1]++; /* Carry from low to high */
- }
- ctx->bits[1] += len >> 29;
-
- t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
-
- /* Handle any leading odd-sized chunks */
-
- if (t)
- {
- unsigned char *p = (unsigned char *) ctx->in + t;
-
- t = 64 - t;
- if (len < t)
- {
- memcpy(p, buf, len);
- return;
- }
- memcpy(p, buf, t);
- byteReverse(ctx->in, 16);
- MD5Transform(ctx->buf, (unsigned int *) ctx->in);
- buf += t;
- len -= t;
- }
- /* Process data in 64-byte chunks */
-
- while (len >= 64)
- {
- memcpy(ctx->in, buf, 64);
- byteReverse(ctx->in, 16);
- MD5Transform(ctx->buf, (unsigned int *) ctx->in);
- buf += 64;
- len -= 64;
- }
-
- /* Handle any remaining bytes of data. */
-
- memcpy(ctx->in, buf, len);
-}
-
-
-/* #define F1(x, y, z) (x & y | ~x & z) */
-#define F1(x, y, z) (z ^ (x & (y ^ z)))
-#define F2(x, y, z) F1(z, x, y)
-#define F3(x, y, z) (x ^ y ^ z)
-#define F4(x, y, z) (y ^ (x | ~z))
-
-/* This is the central step in the MD5 algorithm. */
-#define MD5STEP(f, w, x, y, z, data, s) \
- ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
-
-/*
- * The core of the MD5 algorithm, this alters an existing MD5 hash to
- * reflect the addition of 16 longwords of new data. MD5Update blocks
- * the data and converts bytes into longwords for this routine.
- */
-static void MD5Transform(unsigned int buf[4], unsigned int in[16])
-{
- register unsigned int a, b, c, d;
-
- a = buf[0];
- b = buf[1];
- c = buf[2];
- d = buf[3];
-
- MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
- MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
- MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
- MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
- MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
- MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
- MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
- MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
- MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
- MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
- MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
- MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
- MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
- MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
- MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
- MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
-
- MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
- MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
- MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
- MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
- MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
- MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
- MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
- MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
- MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
- MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
- MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
- MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
- MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
- MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
- MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
- MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
-
- MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
- MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
- MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
- MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
- MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
- MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
- MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
- MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
- MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
- MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
- MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
- MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
- MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
- MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
- MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
- MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
-
- MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
- MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
- MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
- MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
- MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
- MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
- MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
- MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
- MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
- MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
- MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
- MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
- MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
- MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
- MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
- MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
-
- buf[0] += a;
- buf[1] += b;
- buf[2] += c;
- buf[3] += d;
-}
-
-/*
-// ---------------------------
-// RC4 implementation
-// ---------------------------
-*/
-
-/**
-* RC4 is the standard encryption algorithm used in PDF format
-*/
-
-void
-CodecRC4(Codec* codec, unsigned char* key, int keylen,
- unsigned char* textin, int textlen,
- unsigned char* textout)
-{
- int i;
- int j;
- int t;
- unsigned char rc4[256];
-
- int a = 0;
- int b = 0;
- unsigned char k;
-
- for (i = 0; i < 256; i++)
- {
- rc4[i] = i;
- }
- j = 0;
- for (i = 0; i < 256; i++)
- {
- t = rc4[i];
- j = (j + t + key[i % keylen]) % 256;
- rc4[i] = rc4[j];
- rc4[j] = t;
- }
-
- for (i = 0; i < textlen; i++)
- {
- a = (a + 1) % 256;
- t = rc4[a];
- b = (b + t) % 256;
- rc4[a] = rc4[b];
- rc4[b] = t;
- k = rc4[(rc4[a] + rc4[b]) % 256];
- textout[i] = textin[i] ^ k;
- }
-}
-
-void
-CodecGetMD5Binary(Codec* codec, unsigned char* data, int length, unsigned char* digest)
-{
- MD5_CTX ctx;
- MD5Init(&ctx);
- MD5Update(&ctx, data, length);
- MD5Final(digest,&ctx);
-}
-
-#if CODEC_TYPE == CODEC_TYPE_AES256
-void
-CodecGetSHABinary(Codec* codec, unsigned char* data, int length, unsigned char* digest)
-{
- sha256(data, (unsigned int) length, digest);
-}
-#endif
-
-#define MODMULT(a, b, c, m, s) q = s / a; s = b * (s - a * q) - c * q; if (s < 0) s += m
-
-void
-CodecGenerateInitialVector(Codec* codec, int seed, unsigned char iv[16])
-{
- unsigned char initkey[16];
- int j, q;
- int z = seed + 1;
- for (j = 0; j < 4; j++)
- {
- MODMULT(52774, 40692, 3791, 2147483399L, z);
- initkey[4*j+0] = 0xff & z;
- initkey[4*j+1] = 0xff & (z >> 8);
- initkey[4*j+2] = 0xff & (z >> 16);
- initkey[4*j+3] = 0xff & (z >> 24);
- }
- CodecGetMD5Binary(codec, (unsigned char*) initkey, 16, iv);
-}
-
-void
-CodecAES(Codec* codec, int page, int encrypt, unsigned char encryptionKey[KEYLENGTH],
- unsigned char* datain, int datalen, unsigned char* dataout)
-{
- unsigned char initial[16];
- unsigned char pagekey[KEYLENGTH];
- unsigned char nkey[KEYLENGTH+4+4];
- int keyLength = KEYLENGTH;
- int nkeylen = keyLength + 4 + 4;
- int j;
- int direction = (encrypt) ? RIJNDAEL_Direction_Encrypt : RIJNDAEL_Direction_Decrypt;
- int len = 0;
-
- for (j = 0; j < keyLength; j++)
- {
- nkey[j] = encryptionKey[j];
- }
- nkey[keyLength+0] = 0xff & page;
- nkey[keyLength+1] = 0xff & (page >> 8);
- nkey[keyLength+2] = 0xff & (page >> 16);
- nkey[keyLength+3] = 0xff & (page >> 24);
-
- /* AES encryption needs some 'salt' */
- nkey[keyLength+4] = 0x73;
- nkey[keyLength+5] = 0x41;
- nkey[keyLength+6] = 0x6c;
- nkey[keyLength+7] = 0x54;
-
-#if CODEC_TYPE == CODEC_TYPE_AES256
- CodecGetSHABinary(codec, nkey, nkeylen, pagekey);
-#else
- CodecGetMD5Binary(codec, nkey, nkeylen, pagekey);
-#endif
- CodecGenerateInitialVector(codec, page, initial);
-
-#if CODEC_TYPE == CODEC_TYPE_AES256
- RijndaelInit(codec->m_aes, RIJNDAEL_Direction_Mode_CBC, direction, pagekey, RIJNDAEL_Direction_KeyLength_Key32Bytes, initial);
-#else
- RijndaelInit(codec->m_aes, RIJNDAEL_Direction_Mode_CBC, direction, pagekey, RIJNDAEL_Direction_KeyLength_Key16Bytes, initial);
-#endif
- if (encrypt)
- {
- len = RijndaelBlockEncrypt(codec->m_aes, datain, datalen*8, dataout);
- }
- else
- {
- len = RijndaelBlockDecrypt(codec->m_aes, datain, datalen*8, dataout);
- }
-
- /* It is a good idea to check the error code */
- if (len < 0)
- {
- /* AES: Error on encrypting. */
- }
-}
-
-static unsigned char padding[] =
- "\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A";
-
-void
-CodecInit(Codec* codec)
-{
- codec->m_isEncrypted = 0;
- codec->m_hasReadKey = 0;
- codec->m_hasWriteKey = 0;
- codec->m_aes = (Rijndael*) wx_sqlite3_malloc(sizeof(Rijndael));
- RijndaelCreate(codec->m_aes);
-}
-
-void
-CodecTerm(Codec* codec)
-{
- wx_sqlite3_free(codec->m_aes);
-}
-
-void
-CodecSetIsEncrypted(Codec* codec, int isEncrypted)
-{
- codec->m_isEncrypted = isEncrypted;
-}
-
-void
-CodecSetHasReadKey(Codec* codec, int hasReadKey)
-{
- codec->m_hasReadKey = hasReadKey;
-}
-
-void
-CodecSetHasWriteKey(Codec* codec, int hasWriteKey)
-{
- codec->m_hasWriteKey = hasWriteKey;
-}
-
-void
-CodecSetBtree(Codec* codec, Btree* bt)
-{
- codec->m_bt = bt;
-}
-
-int
-CodecIsEncrypted(Codec* codec)
-{
- return codec->m_isEncrypted;
-}
-
-int
-CodecHasReadKey(Codec* codec)
-{
- return codec->m_hasReadKey;
-}
-
-int
-CodecHasWriteKey(Codec* codec)
-{
- return codec->m_hasWriteKey;
-}
-
-Btree*
-CodecGetBtree(Codec* codec)
-{
- return codec->m_bt;
-}
-
-unsigned char*
-CodecGetPageBuffer(Codec* codec)
-{
- return &codec->m_page[4];
-}
-
-void
-CodecCopy(Codec* codec, Codec* other)
-{
- int j;
- codec->m_isEncrypted = other->m_isEncrypted;
- codec->m_hasReadKey = other->m_hasReadKey;
- codec->m_hasWriteKey = other->m_hasWriteKey;
- for (j = 0; j < KEYLENGTH; j++)
- {
- codec->m_readKey[j] = other->m_readKey[j];
- codec->m_writeKey[j] = other->m_writeKey[j];
- }
- codec->m_bt = other->m_bt;
- RijndaelInvalidate(codec->m_aes);
-}
-
-void
-CodecCopyKey(Codec* codec, int read2write)
-{
- int j;
- if (read2write)
- {
- for (j = 0; j < KEYLENGTH; j++)
- {
- codec->m_writeKey[j] = codec->m_readKey[j];
- }
- }
- else
- {
- for (j = 0; j < KEYLENGTH; j++)
- {
- codec->m_readKey[j] = codec->m_writeKey[j];
- }
- }
-}
-
-void
-CodecPadPassword(Codec* codec, char* password, int pswdlen, unsigned char pswd[32])
-{
- int j;
- int p = 0;
- int m = pswdlen;
- if (m > 32) m = 32;
-
- for (j = 0; j < m; j++)
- {
- pswd[p++] = (unsigned char) password[j];
- }
- for (j = 0; p < 32 && j < 32; j++)
- {
- pswd[p++] = padding[j];
- }
-}
-
-void
-CodecGenerateReadKey(Codec* codec, char* userPassword, int passwordLength)
-{
- CodecGenerateEncryptionKey(codec, userPassword, passwordLength, codec->m_readKey);
-}
-
-void
-CodecGenerateWriteKey(Codec* codec, char* userPassword, int passwordLength)
-{
- CodecGenerateEncryptionKey(codec, userPassword, passwordLength, codec->m_writeKey);
-}
-
-void
-CodecGenerateEncryptionKey(Codec* codec, char* userPassword, int passwordLength,
- unsigned char encryptionKey[KEYLENGTH])
-{
-#if CODEC_TYPE == CODEC_TYPE_AES256
- unsigned char userPad[32];
- unsigned char digest[KEYLENGTH];
- int keyLength = KEYLENGTH;
- int k;
-
- /* Pad password */
- CodecPadPassword(codec, userPassword, passwordLength, userPad);
-
- sha256(userPad, 32, digest);
- for (k = 0; k < CODEC_SHA_ITER; ++k)
- {
- sha256(digest, KEYLENGTH, digest);
- }
- memcpy(encryptionKey, digest, keyLength);
-#else
- unsigned char userPad[32];
- unsigned char ownerPad[32];
- unsigned char ownerKey[32];
-
- unsigned char mkey[MD5_HASHBYTES];
- unsigned char digest[MD5_HASHBYTES];
- int keyLength = MD5_HASHBYTES;
- int i, j, k;
- MD5_CTX ctx;
-
- /* Pad passwords */
- CodecPadPassword(codec, userPassword, passwordLength, userPad);
- CodecPadPassword(codec, "", 0, ownerPad);
-
- /* Compute owner key */
-
- MD5Init(&ctx);
- MD5Update(&ctx, ownerPad, 32);
- MD5Final(digest,&ctx);
-
- /* only use for the input as many bit as the key consists of */
- for (k = 0; k < 50; ++k)
- {
- MD5Init(&ctx);
- MD5Update(&ctx, digest, keyLength);
- MD5Final(digest,&ctx);
- }
- memcpy(ownerKey, userPad, 32);
- for (i = 0; i < 20; ++i)
- {
- for (j = 0; j < keyLength ; ++j)
- {
- mkey[j] = (digest[j] ^ i);
- }
- CodecRC4(codec, mkey, keyLength, ownerKey, 32, ownerKey);
- }
-
- /* Compute encryption key */
-
- MD5Init(&ctx);
- MD5Update(&ctx, userPad, 32);
- MD5Update(&ctx, ownerKey, 32);
- MD5Final(digest,&ctx);
-
- /* only use the really needed bits as input for the hash */
- for (k = 0; k < 50; ++k)
- {
- MD5Init(&ctx);
- MD5Update(&ctx, digest, keyLength);
- MD5Final(digest, &ctx);
- }
- memcpy(encryptionKey, digest, keyLength);
-#endif
-}
-
-void
-CodecEncrypt(Codec* codec, int page, unsigned char* data, int len, int useWriteKey)
-{
-#ifdef WXSQLITE3_USE_OLD_ENCRYPTION_SCHEME
- /* Use the previous encryption scheme */
- unsigned char* key = (useWriteKey) ? codec->m_writeKey : codec->m_readKey;
- CodecAES(codec, page, 1, key, data, len, data);
-#else
- unsigned char dbHeader[8];
- int offset = 0;
- unsigned char* key = (useWriteKey) ? codec->m_writeKey : codec->m_readKey;
- if (page == 1)
- {
- /* Save the header bytes remaining unencrypted */
- memcpy(dbHeader, data+16, 8);
- offset = 16;
- CodecAES(codec, page, 1, key, data, 16, data);
- }
- CodecAES(codec, page, 1, key, data+offset, len-offset, data+offset);
- if (page == 1)
- {
- /* Move the encrypted header bytes 16..23 to a safe position */
- memcpy(data+8, data+16, 8);
- /* Restore the unencrypted header bytes 16..23 */
- memcpy(data+16, dbHeader, 8);
- }
-#endif
-}
-
-void
-CodecDecrypt(Codec* codec, int page, unsigned char* data, int len)
-{
-#ifdef WXSQLITE3_USE_OLD_ENCRYPTION_SCHEME
- /* Use the previous encryption scheme */
- CodecAES(codec, page, 0, codec->m_readKey, data, len, data);
-#else
- unsigned char dbHeader[8];
- int dbPageSize;
- int offset = 0;
- if (page == 1)
- {
- /* Save (unencrypted) header bytes 16..23 */
- memcpy(dbHeader, data+16, 8);
- /* Determine page size */
- dbPageSize = (dbHeader[0] << 8) | (dbHeader[1] << 16);
- /* Check whether the database header is valid */
- /* If yes, the database follows the new encryption scheme, otherwise use the previous encryption scheme */
- if ((dbPageSize >= 512) && (dbPageSize <= SQLITE_MAX_PAGE_SIZE) && (((dbPageSize-1) & dbPageSize) == 0) &&
- (dbHeader[5] == 0x40) && (dbHeader[6] == 0x20) && (dbHeader[7] == 0x20))
- {
- /* Restore encrypted bytes 16..23 for new encryption scheme */
- memcpy(data+16, data+8, 8);
- offset = 16;
- }
- }
- CodecAES(codec, page, 0, codec->m_readKey, data+offset, len-offset, data+offset);
- if (page == 1 && offset != 0)
- {
- /* Verify the database header */
- if (memcmp(dbHeader, data+16, 8) == 0)
- {
- memcpy(data, SQLITE_FILE_HEADER, 16);
- }
- }
-#endif
-}
-
-
+/*
+** Name: codec.c
+** Purpose: Implementation of SQLite codecs
+** Author: Ulrich Telle
+** Created: 2006-12-06
+** Copyright: (c) 2006-2018 Ulrich Telle
+** License: LGPL-3.0+ WITH WxWindows-exception-3.1
+*/
+
+#include "codec.h"
+
+/*
+** RC4 implementation
+*/
+
+void
+CodecRC4(unsigned char* key, int keylen,
+ unsigned char* textin, int textlen,
+ unsigned char* textout)
+{
+ int i;
+ int j;
+ int t;
+ unsigned char rc4[256];
+
+ int a = 0;
+ int b = 0;
+ unsigned char k;
+
+ for (i = 0; i < 256; i++)
+ {
+ rc4[i] = i;
+ }
+ j = 0;
+ for (i = 0; i < 256; i++)
+ {
+ t = rc4[i];
+ j = (j + t + key[i % keylen]) % 256;
+ rc4[i] = rc4[j];
+ rc4[j] = t;
+ }
+
+ for (i = 0; i < textlen; i++)
+ {
+ a = (a + 1) % 256;
+ t = rc4[a];
+ b = (b + t) % 256;
+ rc4[a] = rc4[b];
+ rc4[b] = t;
+ k = rc4[(rc4[a] + rc4[b]) % 256];
+ textout[i] = textin[i] ^ k;
+ }
+}
+
+void
+CodecGetMD5Binary(unsigned char* data, int length, unsigned char* digest)
+{
+ MD5_CTX ctx;
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, data, length);
+ MD5_Final(digest,&ctx);
+}
+
+void
+CodecGetSHABinary(unsigned char* data, int length, unsigned char* digest)
+{
+ sha256(data, (unsigned int) length, digest);
+}
+
+#define MODMULT(a, b, c, m, s) q = s / a; s = b * (s - a * q) - c * q; if (s < 0) s += m
+
+void
+CodecGenerateInitialVector(int seed, unsigned char iv[16])
+{
+ unsigned char initkey[16];
+ int j, q;
+ int z = seed + 1;
+ for (j = 0; j < 4; j++)
+ {
+ MODMULT(52774, 40692, 3791, 2147483399L, z);
+ initkey[4*j+0] = 0xff & z;
+ initkey[4*j+1] = 0xff & (z >> 8);
+ initkey[4*j+2] = 0xff & (z >> 16);
+ initkey[4*j+3] = 0xff & (z >> 24);
+ }
+ CodecGetMD5Binary((unsigned char*) initkey, 16, iv);
+}
+
+int
+CodecAES128(Rijndael* aesCtx, int page, int encrypt, unsigned char encryptionKey[KEYLENGTH_AES128],
+ unsigned char* datain, int datalen, unsigned char* dataout)
+{
+ int rc = SQLITE_OK;
+ unsigned char initial[16];
+ unsigned char pagekey[KEYLENGTH_AES128];
+ unsigned char nkey[KEYLENGTH_AES128+4+4];
+ int keyLength = KEYLENGTH_AES128;
+ int nkeylen = keyLength + 4 + 4;
+ int j;
+ int direction = (encrypt) ? RIJNDAEL_Direction_Encrypt : RIJNDAEL_Direction_Decrypt;
+ int len = 0;
+
+ for (j = 0; j < keyLength; j++)
+ {
+ nkey[j] = encryptionKey[j];
+ }
+ nkey[keyLength+0] = 0xff & page;
+ nkey[keyLength+1] = 0xff & (page >> 8);
+ nkey[keyLength+2] = 0xff & (page >> 16);
+ nkey[keyLength+3] = 0xff & (page >> 24);
+
+ /* AES encryption needs some 'salt' */
+ nkey[keyLength+4] = 0x73;
+ nkey[keyLength+5] = 0x41;
+ nkey[keyLength+6] = 0x6c;
+ nkey[keyLength+7] = 0x54;
+
+ CodecGetMD5Binary(nkey, nkeylen, pagekey);
+ CodecGenerateInitialVector(page, initial);
+ RijndaelInit(aesCtx, RIJNDAEL_Direction_Mode_CBC, direction, pagekey, RIJNDAEL_Direction_KeyLength_Key16Bytes, initial);
+ if (encrypt)
+ {
+ len = RijndaelBlockEncrypt(aesCtx, datain, datalen*8, dataout);
+ }
+ else
+ {
+ len = RijndaelBlockDecrypt(aesCtx, datain, datalen*8, dataout);
+ }
+
+ /* It is a good idea to check the error code */
+ if (len < 0)
+ {
+ /* AES: Error on encrypting. */
+ rc = SQLITE_ERROR;
+ }
+ return rc;
+}
+
+int
+CodecAES256(Rijndael* aesCtx, int page, int encrypt, unsigned char encryptionKey[KEYLENGTH_AES256],
+ unsigned char* datain, int datalen, unsigned char* dataout)
+{
+ int rc = SQLITE_OK;
+ unsigned char initial[16];
+ unsigned char pagekey[KEYLENGTH_AES256];
+ unsigned char nkey[KEYLENGTH_AES256+4+4];
+ int keyLength = KEYLENGTH_AES256;
+ int nkeylen = keyLength + 4 + 4;
+ int j;
+ int direction = (encrypt) ? RIJNDAEL_Direction_Encrypt : RIJNDAEL_Direction_Decrypt;
+ int len = 0;
+
+ for (j = 0; j < keyLength; j++)
+ {
+ nkey[j] = encryptionKey[j];
+ }
+ nkey[keyLength+0] = 0xff & page;
+ nkey[keyLength+1] = 0xff & (page >> 8);
+ nkey[keyLength+2] = 0xff & (page >> 16);
+ nkey[keyLength+3] = 0xff & (page >> 24);
+
+ /* AES encryption needs some 'salt' */
+ nkey[keyLength+4] = 0x73;
+ nkey[keyLength+5] = 0x41;
+ nkey[keyLength+6] = 0x6c;
+ nkey[keyLength+7] = 0x54;
+
+ CodecGetSHABinary(nkey, nkeylen, pagekey);
+ CodecGenerateInitialVector(page, initial);
+ RijndaelInit(aesCtx, RIJNDAEL_Direction_Mode_CBC, direction, pagekey, RIJNDAEL_Direction_KeyLength_Key32Bytes, initial);
+ if (encrypt)
+ {
+ len = RijndaelBlockEncrypt(aesCtx, datain, datalen*8, dataout);
+ }
+ else
+ {
+ len = RijndaelBlockDecrypt(aesCtx, datain, datalen*8, dataout);
+ }
+
+ /* It is a good idea to check the error code */
+ if (len < 0)
+ {
+ /* AES: Error on encrypting. */
+ rc = SQLITE_ERROR;
+ }
+ return rc;
+}
+
+static unsigned char padding[] =
+ "\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A";
+
+/* --- Codec Descriptor Table --- */
+
+#define CIPHER_PARAMS_SENTINEL { "", 0, 0, 0, 0 }
+#define CIPHER_PAGE1_OFFSET 24
+
+typedef struct _CipherParams
+{
+ char* m_name;
+ int m_value;
+ int m_default;
+ int m_minValue;
+ int m_maxValue;
+} CipherParams;
+
+/*
+** Common configuration parameters
+**
+** - cipher : default cipher type
+*/
+
+CipherParams commonParams[] =
+{
+ { "cipher", CODEC_TYPE, CODEC_TYPE, 1, CODEC_TYPE_MAX },
+ CIPHER_PARAMS_SENTINEL
+};
+
+/*
+** Configuration parameters for "aes128cbc"
+**
+** - legacy mode : compatibility with first version (page 1 encrypted)
+** possible values: 1 = yes, 0 = no (default)
+*/
+
+#ifdef WXSQLITE3_USE_OLD_ENCRYPTION_SCHEME
+#define AES128_LEGACY_DEFAULT 1
+#else
+#define AES128_LEGACY_DEFAULT 0
+#endif
+
+CipherParams aes128Params[] =
+{
+ { "legacy", AES128_LEGACY_DEFAULT, AES128_LEGACY_DEFAULT, 0, 1 },
+ { "legacy_page_size", 0, 0, 0, SQLITE_MAX_PAGE_SIZE },
+ CIPHER_PARAMS_SENTINEL
+};
+
+/*
+** Configuration parameters for "aes256cbc"
+**
+** - legacy mode : compatibility with first version (page 1 encrypted)
+** possible values: 1 = yes, 0 = no (default)
+** - kdf_iter : number of iterations for key derivation
+*/
+
+#ifdef WXSQLITE3_USE_OLD_ENCRYPTION_SCHEME
+#define AES256_LEGACY_DEFAULT 1
+#else
+#define AES256_LEGACY_DEFAULT 0
+#endif
+
+CipherParams aes256Params[] =
+{
+ { "legacy", AES256_LEGACY_DEFAULT, AES256_LEGACY_DEFAULT, 0, 1 },
+ { "legacy_page_size", 0, 0, 0, SQLITE_MAX_PAGE_SIZE },
+ { "kdf_iter", CODEC_SHA_ITER, CODEC_SHA_ITER, 1, 0x7fffffff },
+ CIPHER_PARAMS_SENTINEL
+};
+
+/*
+** Configuration parameters for "chacha20"
+**
+** - legacy mode : compatibility with original sqleet
+** (page 1 encrypted, kdf_iter = 12345)
+** possible values: 1 = yes, 0 = no
+** - kdf_iter : number of iterations for key derivation
+*/
+
+#ifdef WXSQLITE3_USE_SQLEET_LEGACY
+#define CHACHA20_LEGACY_DEFAULT 1
+#else
+#define CHACHA20_LEGACY_DEFAULT 0
+#endif
+
+#define CHACHA20_KDF_ITER_DEFAULT 64007
+#define SQLEET_KDF_ITER 12345
+#define CHACHA20_LEGACY_PAGE_SIZE 4096
+
+CipherParams chacha20Params[] =
+{
+ { "legacy", CHACHA20_LEGACY_DEFAULT, CHACHA20_LEGACY_DEFAULT, 0, 1 },
+ { "legacy_page_size", CHACHA20_LEGACY_PAGE_SIZE, CHACHA20_LEGACY_PAGE_SIZE, 0, SQLITE_MAX_PAGE_SIZE },
+ { "kdf_iter", CHACHA20_KDF_ITER_DEFAULT, CHACHA20_KDF_ITER_DEFAULT, 1, 0x7fffffff },
+ CIPHER_PARAMS_SENTINEL
+};
+
+/*
+** Configuration parameters for "sqlcipher"
+**
+** - kdf_iter : number of iterations for key derivation
+** - fast_kdf_iter : number of iterations for hmac key
+** - hmac_use : flag whether to use hmac
+** - hmac_pgno : storage type for page number in hmac (native, le, be)
+** - hmac_salt_mask : mask byte for hmac salt
+*/
+
+#ifdef WXSQLITE3_USE_SQLCIPHER_LEGACY
+#define SQLCIPHER_LEGACY_DEFAULT 1
+#else
+#define SQLCIPHER_LEGACY_DEFAULT 0
+#endif
+
+#define SQLCIPHER_KDF_ITER 64000
+#define SQLCIPHER_FAST_KDF_ITER 2
+#define SQLCIPHER_HMAC_USE 1
+#define SQLCIPHER_HMAC_PGNO_LE 1
+#define SQLCIPHER_HMAC_PGNO_BE 2
+#define SQLCIPHER_HMAC_PGNO_NATIVE 0
+#define SQLCIPHER_HMAC_SALT_MASK 0x3a
+#define SQLCIPHER_LEGACY_PAGE_SIZE 1024
+
+CipherParams sqlCipherParams[] =
+{
+ { "legacy", SQLCIPHER_LEGACY_DEFAULT, SQLCIPHER_LEGACY_DEFAULT, 0, 1 },
+ { "legacy_page_size", SQLCIPHER_LEGACY_PAGE_SIZE, SQLCIPHER_LEGACY_PAGE_SIZE, 0, SQLITE_MAX_PAGE_SIZE },
+ { "kdf_iter", SQLCIPHER_KDF_ITER, SQLCIPHER_KDF_ITER, 1, 0x7fffffff },
+ { "fast_kdf_iter", SQLCIPHER_FAST_KDF_ITER, SQLCIPHER_FAST_KDF_ITER, 1, 0x7fffffff },
+ { "hmac_use", SQLCIPHER_HMAC_USE, SQLCIPHER_HMAC_USE, 0, 1 },
+ { "hmac_pgno", SQLCIPHER_HMAC_PGNO_LE, SQLCIPHER_HMAC_PGNO_LE, 0, 2 },
+ { "hmac_salt_mask", SQLCIPHER_HMAC_SALT_MASK, SQLCIPHER_HMAC_SALT_MASK, 0x00, 0xff },
+ CIPHER_PARAMS_SENTINEL
+};
+
+int
+GetCipherParameter(CipherParams* cipherParams, const char* paramName)
+{
+ int value = -1;
+ for (; strlen(cipherParams->m_name) > 0; ++cipherParams)
+ {
+ if (wx_sqlite3_stricmp(paramName, cipherParams->m_name) == 0) break;
+ }
+ if (strlen(cipherParams->m_name) > 0)
+ {
+ value = cipherParams->m_value;
+ cipherParams->m_value = cipherParams->m_default;
+ }
+ return value;
+}
+
+/* --- AES 128-bit cipher (wxSQLite3) --- */
+
+typedef struct _AES128Cipher
+{
+ int m_legacy;
+ int m_legacyPageSize;
+ int m_keyLength;
+ unsigned char m_key[KEYLENGTH_AES128];
+ Rijndael* m_aes;
+} AES128Cipher;
+
+void*
+AllocateAES128Cipher(wx_sqlite3* db)
+{
+ AES128Cipher* aesCipher = (AES128Cipher*) wx_sqlite3_malloc(sizeof(AES128Cipher));
+ if (aesCipher != NULL)
+ {
+ aesCipher->m_aes = (Rijndael*) wx_sqlite3_malloc(sizeof(Rijndael));
+ if (aesCipher->m_aes != NULL)
+ {
+ aesCipher->m_keyLength = KEYLENGTH_AES128;
+ memset(aesCipher->m_key, 0, KEYLENGTH_AES128);
+ RijndaelCreate(aesCipher->m_aes);
+ }
+ else
+ {
+ wx_sqlite3_free(aesCipher);
+ aesCipher = NULL;
+ }
+ }
+ if (aesCipher != NULL)
+ {
+ CipherParams* cipherParams = (CipherParams*) GetCipherParams(db, CODEC_TYPE_AES128);
+ aesCipher->m_legacy = GetCipherParameter(cipherParams, "legacy");
+ aesCipher->m_legacyPageSize = GetCipherParameter(cipherParams, "legacy_page_size");
+ }
+ return aesCipher;
+}
+
+void
+FreeAES128Cipher(void* cipher)
+{
+ AES128Cipher* localCipher = (AES128Cipher*) cipher;
+ memset(localCipher->m_aes, 0, sizeof(Rijndael));
+ wx_sqlite3_free(localCipher->m_aes);
+ memset(localCipher, 0, sizeof(AES128Cipher));
+ wx_sqlite3_free(localCipher);
+}
+
+void
+CloneAES128Cipher(void* cipherTo, void* cipherFrom)
+{
+ AES128Cipher* aesCipherTo = (AES128Cipher*) cipherTo;
+ AES128Cipher* aesCipherFrom = (AES128Cipher*) cipherFrom;
+ aesCipherTo->m_legacy = aesCipherFrom->m_legacy;
+ aesCipherTo->m_legacyPageSize = aesCipherFrom->m_legacyPageSize;
+ aesCipherTo->m_keyLength = aesCipherFrom->m_keyLength;
+ memcpy(aesCipherTo->m_key, aesCipherFrom->m_key, KEYLENGTH_AES128);
+ RijndaelInvalidate(aesCipherTo->m_aes);
+ RijndaelInvalidate(aesCipherFrom->m_aes);
+}
+
+int
+GetLegacyAES128Cipher(void* cipher)
+{
+ AES128Cipher* aesCipher = (AES128Cipher*)cipher;
+ return aesCipher->m_legacy;
+}
+
+int
+GetPageSizeAES128Cipher(void* cipher)
+{
+ AES128Cipher* aesCipher = (AES128Cipher*) cipher;
+ int pageSize = 0;
+ if (aesCipher->m_legacy != 0)
+ {
+ pageSize = aesCipher->m_legacyPageSize;
+ if ((pageSize < 512) || (pageSize > SQLITE_MAX_PAGE_SIZE) || (((pageSize - 1) & pageSize) != 0))
+ {
+ pageSize = 0;
+ }
+ }
+ return pageSize;
+}
+
+int
+GetReservedAES128Cipher(void* cipher)
+{
+ return 0;
+}
+
+void
+GenerateKeyAES128Cipher(void* cipher, Btree* pBt, char* userPassword, int passwordLength, int rekey)
+{
+ AES128Cipher* aesCipher = (AES128Cipher*) cipher;
+ unsigned char userPad[32];
+ unsigned char ownerPad[32];
+ unsigned char ownerKey[32];
+
+ unsigned char mkey[MD5_HASHBYTES];
+ unsigned char digest[MD5_HASHBYTES];
+ int keyLength = MD5_HASHBYTES;
+ int i, j, k;
+ MD5_CTX ctx;
+
+ /* Pad passwords */
+ CodecPadPassword(userPassword, passwordLength, userPad);
+ CodecPadPassword("", 0, ownerPad);
+
+ /* Compute owner key */
+
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, ownerPad, 32);
+ MD5_Final(digest, &ctx);
+
+ /* only use for the input as many bit as the key consists of */
+ for (k = 0; k < 50; ++k)
+ {
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, digest, keyLength);
+ MD5_Final(digest, &ctx);
+ }
+ memcpy(ownerKey, userPad, 32);
+ for (i = 0; i < 20; ++i)
+ {
+ for (j = 0; j < keyLength; ++j)
+ {
+ mkey[j] = (digest[j] ^ i);
+ }
+ CodecRC4(mkey, keyLength, ownerKey, 32, ownerKey);
+ }
+
+ /* Compute encryption key */
+
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, userPad, 32);
+ MD5_Update(&ctx, ownerKey, 32);
+ MD5_Final(digest, &ctx);
+
+ /* only use the really needed bits as input for the hash */
+ for (k = 0; k < 50; ++k)
+ {
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, digest, keyLength);
+ MD5_Final(digest, &ctx);
+ }
+ memcpy(aesCipher->m_key, digest, aesCipher->m_keyLength);
+}
+
+int
+EncryptPageAES128Cipher(void* cipher, int page, unsigned char* data, int len, int reserved)
+{
+ AES128Cipher* aesCipher = (AES128Cipher*) cipher;
+ int rc = SQLITE_OK;
+ if (aesCipher->m_legacy != 0)
+ {
+ /* Use the legacy encryption scheme */
+ unsigned char* key = aesCipher->m_key;
+ rc = CodecAES128(aesCipher->m_aes, page, 1, key, data, len, data);
+ }
+ else
+ {
+ unsigned char dbHeader[8];
+ int offset = 0;
+ unsigned char* key = aesCipher->m_key;
+ if (page == 1)
+ {
+ /* Save the header bytes remaining unencrypted */
+ memcpy(dbHeader, data + 16, 8);
+ offset = 16;
+ CodecAES128(aesCipher->m_aes, page, 1, key, data, 16, data);
+ }
+ rc = CodecAES128(aesCipher->m_aes, page, 1, key, data + offset, len - offset, data + offset);
+ if (page == 1)
+ {
+ /* Move the encrypted header bytes 16..23 to a safe position */
+ memcpy(data + 8, data + 16, 8);
+ /* Restore the unencrypted header bytes 16..23 */
+ memcpy(data + 16, dbHeader, 8);
+ }
+ }
+ return rc;
+}
+
+int
+DecryptPageAES128Cipher(void* cipher, int page, unsigned char* data, int len, int reserved)
+{
+ AES128Cipher* aesCipher = (AES128Cipher*) cipher;
+ int rc = SQLITE_OK;
+ if (aesCipher->m_legacy != 0)
+ {
+ /* Use the legacy encryption scheme */
+ rc = CodecAES128(aesCipher->m_aes, page, 0, aesCipher->m_key, data, len, data);
+ }
+ else
+ {
+ unsigned char dbHeader[8];
+ int dbPageSize;
+ int offset = 0;
+ if (page == 1)
+ {
+ /* Save (unencrypted) header bytes 16..23 */
+ memcpy(dbHeader, data + 16, 8);
+ /* Determine page size */
+ dbPageSize = (dbHeader[0] << 8) | (dbHeader[1] << 16);
+ /* Check whether the database header is valid */
+ /* If yes, the database follows the new encryption scheme, otherwise use the previous encryption scheme */
+ if ((dbPageSize >= 512) && (dbPageSize <= SQLITE_MAX_PAGE_SIZE) && (((dbPageSize - 1) & dbPageSize) == 0) &&
+ (dbHeader[5] == 0x40) && (dbHeader[6] == 0x20) && (dbHeader[7] == 0x20))
+ {
+ /* Restore encrypted bytes 16..23 for new encryption scheme */
+ memcpy(data + 16, data + 8, 8);
+ offset = 16;
+ }
+ }
+ rc = CodecAES128(aesCipher->m_aes, page, 0, aesCipher->m_key, data + offset, len - offset, data + offset);
+ if (page == 1 && offset != 0)
+ {
+ /* Verify the database header */
+ if (memcmp(dbHeader, data + 16, 8) == 0)
+ {
+ memcpy(data, SQLITE_FILE_HEADER, 16);
+ }
+ }
+ }
+ return rc;
+}
+
+/* --- AES 256-bit cipher (wxSQLite3) --- */
+
+typedef struct _AES256Cipher
+{
+ int m_legacy;
+ int m_legacyPageSize;
+ int m_kdfIter;
+ int m_keyLength;
+ unsigned char m_key[KEYLENGTH_AES256];
+ Rijndael* m_aes;
+} AES256Cipher;
+
+void*
+AllocateAES256Cipher(wx_sqlite3* db)
+{
+ AES256Cipher* aesCipher = (AES256Cipher*) wx_sqlite3_malloc(sizeof(AES256Cipher));
+ if (aesCipher != NULL)
+ {
+ aesCipher->m_aes = (Rijndael*) wx_sqlite3_malloc(sizeof(Rijndael));
+ if (aesCipher->m_aes != NULL)
+ {
+ aesCipher->m_keyLength = KEYLENGTH_AES256;
+ memset(aesCipher->m_key, 0, KEYLENGTH_AES256);
+ RijndaelCreate(aesCipher->m_aes);
+ }
+ else
+ {
+ wx_sqlite3_free(aesCipher);
+ aesCipher = NULL;
+ }
+ }
+ if (aesCipher != NULL)
+ {
+ CipherParams* cipherParams = (CipherParams*) GetCipherParams(db, CODEC_TYPE_AES256);
+ aesCipher->m_legacy = GetCipherParameter(cipherParams, "legacy");
+ aesCipher->m_legacyPageSize = GetCipherParameter(cipherParams, "legacy_page_size");
+ aesCipher->m_kdfIter = GetCipherParameter(cipherParams, "kdf_iter");
+ }
+ return aesCipher;
+}
+
+void
+FreeAES256Cipher(void* cipher)
+{
+ AES256Cipher* aesCipher = (AES256Cipher*) cipher;
+ memset(aesCipher->m_aes, 0, sizeof(Rijndael));
+ wx_sqlite3_free(aesCipher->m_aes);
+ memset(aesCipher, 0, sizeof(AES256Cipher));
+ wx_sqlite3_free(aesCipher);
+}
+
+void
+CloneAES256Cipher(void* cipherTo, void* cipherFrom)
+{
+ AES256Cipher* aesCipherTo = (AES256Cipher*) cipherTo;
+ AES256Cipher* aesCipherFrom = (AES256Cipher*) cipherFrom;
+ aesCipherTo->m_legacy = aesCipherFrom->m_legacy;
+ aesCipherTo->m_legacyPageSize = aesCipherFrom->m_legacyPageSize;
+ aesCipherTo->m_kdfIter = aesCipherFrom->m_kdfIter;
+ aesCipherTo->m_keyLength = aesCipherFrom->m_keyLength;
+ memcpy(aesCipherTo->m_key, aesCipherFrom->m_key, KEYLENGTH_AES256);
+ RijndaelInvalidate(aesCipherTo->m_aes);
+ RijndaelInvalidate(aesCipherFrom->m_aes);
+}
+
+int
+GetLegacyAES256Cipher(void* cipher)
+{
+ AES256Cipher* aesCipher = (AES256Cipher*)cipher;
+ return aesCipher->m_legacy;
+}
+
+int
+GetPageSizeAES256Cipher(void* cipher)
+{
+ AES256Cipher* aesCipher = (AES256Cipher*) cipher;
+ int pageSize = 0;
+ if (aesCipher->m_legacy != 0)
+ {
+ pageSize = aesCipher->m_legacyPageSize;
+ if ((pageSize < 512) || (pageSize > SQLITE_MAX_PAGE_SIZE) || (((pageSize - 1) & pageSize) != 0))
+ {
+ pageSize = 0;
+ }
+ }
+ return pageSize;
+}
+
+int
+GetReservedAES256Cipher(void* cipher)
+{
+ return 0;
+}
+
+void
+GenerateKeyAES256Cipher(void* cipher, Btree* pBt, char* userPassword, int passwordLength, int rekey)
+{
+ AES256Cipher* aesCipher = (AES256Cipher*) cipher;
+ unsigned char userPad[32];
+ unsigned char digest[KEYLENGTH_AES256];
+ int keyLength = KEYLENGTH_AES256;
+ int k;
+
+ /* Pad password */
+ CodecPadPassword(userPassword, passwordLength, userPad);
+
+ sha256(userPad, 32, digest);
+ for (k = 0; k < CODEC_SHA_ITER; ++k)
+ {
+ sha256(digest, KEYLENGTH_AES256, digest);
+ }
+ memcpy(aesCipher->m_key, digest, aesCipher->m_keyLength);
+}
+
+int
+EncryptPageAES256Cipher(void* cipher, int page, unsigned char* data, int len, int reserved)
+{
+ AES256Cipher* aesCipher = (AES256Cipher*) cipher;
+ int rc = SQLITE_OK;
+ if (aesCipher->m_legacy != 0)
+ {
+ /* Use the legacy encryption scheme */
+ unsigned char* key = aesCipher->m_key;
+ rc = CodecAES256(aesCipher->m_aes, page, 1, key, data, len, data);
+ }
+ else
+ {
+ unsigned char dbHeader[8];
+ int offset = 0;
+ unsigned char* key = aesCipher->m_key;
+ if (page == 1)
+ {
+ /* Save the header bytes remaining unencrypted */
+ memcpy(dbHeader, data + 16, 8);
+ offset = 16;
+ CodecAES256(aesCipher->m_aes, page, 1, key, data, 16, data);
+ }
+ rc = CodecAES256(aesCipher->m_aes, page, 1, key, data + offset, len - offset, data + offset);
+ if (page == 1)
+ {
+ /* Move the encrypted header bytes 16..23 to a safe position */
+ memcpy(data + 8, data + 16, 8);
+ /* Restore the unencrypted header bytes 16..23 */
+ memcpy(data + 16, dbHeader, 8);
+ }
+ }
+ return rc;
+}
+
+int
+DecryptPageAES256Cipher(void* cipher, int page, unsigned char* data, int len, int reserved)
+{
+ AES256Cipher* aesCipher = (AES256Cipher*) cipher;
+ int rc = SQLITE_OK;
+ if (aesCipher->m_legacy != 0)
+ {
+ /* Use the legacy encryption scheme */
+ rc = CodecAES256(aesCipher->m_aes, page, 0, aesCipher->m_key, data, len, data);
+ }
+ else
+ {
+ unsigned char dbHeader[8];
+ int dbPageSize;
+ int offset = 0;
+ if (page == 1)
+ {
+ /* Save (unencrypted) header bytes 16..23 */
+ memcpy(dbHeader, data + 16, 8);
+ /* Determine page size */
+ dbPageSize = (dbHeader[0] << 8) | (dbHeader[1] << 16);
+ /* Check whether the database header is valid */
+ /* If yes, the database follows the new encryption scheme, otherwise use the previous encryption scheme */
+ if ((dbPageSize >= 512) && (dbPageSize <= SQLITE_MAX_PAGE_SIZE) && (((dbPageSize - 1) & dbPageSize) == 0) &&
+ (dbHeader[5] == 0x40) && (dbHeader[6] == 0x20) && (dbHeader[7] == 0x20))
+ {
+ /* Restore encrypted bytes 16..23 for new encryption scheme */
+ memcpy(data + 16, data + 8, 8);
+ offset = 16;
+ }
+ }
+ rc = CodecAES256(aesCipher->m_aes, page, 0, aesCipher->m_key, data + offset, len - offset, data + offset);
+ if (page == 1 && offset != 0)
+ {
+ /* Verify the database header */
+ if (memcmp(dbHeader, data + 16, 8) == 0)
+ {
+ memcpy(data, SQLITE_FILE_HEADER, 16);
+ }
+ }
+ }
+ return rc;
+}
+
+/* --- ChaCha20-Poly1305 cipher (plus sqleet variant) --- */
+
+#define KEYLENGTH_CHACHA20 32
+#define SALTLENGTH_CHACHA20 16
+#define PAGE_NONCE_LEN_CHACHA20 16
+#define PAGE_TAG_LEN_CHACHA20 16
+#define PAGE_RESERVED_CHACHA20 (PAGE_NONCE_LEN_CHACHA20 + PAGE_TAG_LEN_CHACHA20)
+
+typedef struct _chacha20Cipher
+{
+ int m_legacy;
+ int m_legacyPageSize;
+ int m_kdfIter;
+ int m_keyLength;
+ unsigned char m_key[KEYLENGTH_CHACHA20];
+ unsigned char m_salt[SALTLENGTH_CHACHA20];
+} ChaCha20Cipher;
+
+void*
+AllocateChaCha20Cipher(wx_sqlite3* db)
+{
+ ChaCha20Cipher* chacha20Cipher = (ChaCha20Cipher*) wx_sqlite3_malloc(sizeof(ChaCha20Cipher));
+ if (chacha20Cipher != NULL)
+ {
+ chacha20Cipher->m_keyLength = KEYLENGTH_CHACHA20;
+ memset(chacha20Cipher->m_key, 0, KEYLENGTH_CHACHA20);
+ memset(chacha20Cipher->m_salt, 0, SALTLENGTH_CHACHA20);
+ }
+ if (chacha20Cipher != NULL)
+ {
+ CipherParams* cipherParams = (CipherParams*) GetCipherParams(db, CODEC_TYPE_CHACHA20);
+ chacha20Cipher->m_legacy = GetCipherParameter(cipherParams, "legacy");
+ chacha20Cipher->m_legacyPageSize = GetCipherParameter(cipherParams, "legacy_page_size");
+ chacha20Cipher->m_kdfIter = GetCipherParameter(cipherParams, "kdf_iter");
+ if (chacha20Cipher->m_legacy != 0)
+ {
+ chacha20Cipher->m_kdfIter = SQLEET_KDF_ITER;
+ }
+ }
+ return chacha20Cipher;
+}
+
+void
+FreeChaCha20Cipher(void* cipher)
+{
+ ChaCha20Cipher* chacha20Cipher = (ChaCha20Cipher*) cipher;
+ memset(chacha20Cipher, 0, sizeof(ChaCha20Cipher));
+ wx_sqlite3_free(chacha20Cipher);
+}
+
+void
+CloneChaCha20Cipher(void* cipherTo, void* cipherFrom)
+{
+ ChaCha20Cipher* chacha20CipherTo = (ChaCha20Cipher*) cipherTo;
+ ChaCha20Cipher* chacha20CipherFrom = (ChaCha20Cipher*) cipherFrom;
+ chacha20CipherTo->m_legacy = chacha20CipherFrom->m_legacy;
+ chacha20CipherTo->m_legacyPageSize = chacha20CipherFrom->m_legacyPageSize;
+ chacha20CipherTo->m_kdfIter = chacha20CipherFrom->m_kdfIter;
+ chacha20CipherTo->m_keyLength = chacha20CipherFrom->m_keyLength;
+ memcpy(chacha20CipherTo->m_key, chacha20CipherFrom->m_key, KEYLENGTH_CHACHA20);
+ memcpy(chacha20CipherTo->m_salt, chacha20CipherFrom->m_salt, SALTLENGTH_CHACHA20);
+}
+
+int
+GetLegacyChaCha20Cipher(void* cipher)
+{
+ ChaCha20Cipher* chacha20Cipher = (ChaCha20Cipher*)cipher;
+ return chacha20Cipher->m_legacy;
+}
+
+int
+GetPageSizeChaCha20Cipher(void* cipher)
+{
+ ChaCha20Cipher* chacha20Cipher = (ChaCha20Cipher*) cipher;
+ int pageSize = 0;
+ if (chacha20Cipher->m_legacy != 0)
+ {
+ pageSize = chacha20Cipher->m_legacyPageSize;
+ if ((pageSize < 512) || (pageSize > SQLITE_MAX_PAGE_SIZE) || (((pageSize - 1) & pageSize) != 0))
+ {
+ pageSize = 0;
+ }
+ }
+ return pageSize;
+}
+
+int
+GetReservedChaCha20Cipher(void* cipher)
+{
+ return PAGE_RESERVED_CHACHA20;
+}
+
+void
+GenerateKeyChaCha20Cipher(void* cipher, Btree* pBt, char* userPassword, int passwordLength, int rekey)
+{
+ ChaCha20Cipher* chacha20Cipher = (ChaCha20Cipher*) cipher;
+
+ Pager *pPager = pBt->pBt->pPager;
+ wx_sqlite3_file* fd = (isOpen(pPager->fd)) ? pPager->fd : NULL;
+
+ if (rekey || fd == NULL || wx_sqlite3OsRead(fd, chacha20Cipher->m_salt, SALTLENGTH_CHACHA20, 0) != SQLITE_OK)
+ {
+ chacha20_rng(chacha20Cipher->m_salt, SALTLENGTH_CHACHA20);
+ }
+ fastpbkdf2_hmac_sha256((unsigned char*) userPassword, passwordLength,
+ chacha20Cipher->m_salt, SALTLENGTH_CHACHA20,
+ chacha20Cipher->m_kdfIter,
+ chacha20Cipher->m_key, KEYLENGTH_CHACHA20);
+}
+
+int
+EncryptPageChaCha20Cipher(void* cipher, int page, unsigned char* data, int len, int reserved)
+{
+ ChaCha20Cipher* chacha20Cipher = (ChaCha20Cipher*) cipher;
+ int rc = SQLITE_OK;
+ int legacy = chacha20Cipher->m_legacy;
+ int nReserved = (reserved == 0 && legacy == 0) ? 0 : GetReservedChaCha20Cipher(cipher);
+ int n = len - nReserved;
+
+ /* Generate one-time keys */
+ unsigned char otk[64];
+ uint32_t counter;
+ int offset;
+
+ /* Check whether number of required reserved bytes and actually reserved bytes match */
+ if ((legacy == 0 && nReserved > reserved) || ((legacy != 0 && nReserved != reserved)))
+ {
+ return SQLITE_CORRUPT;
+ }
+
+ if (nReserved > 0)
+ {
+ /* Encrypt and authenticate */
+ memset(otk, 0, 64);
+ chacha20_rng(data + n, PAGE_NONCE_LEN_CHACHA20);
+ counter = LOAD32_LE(data + n + PAGE_NONCE_LEN_CHACHA20 - 4) ^ page;
+ chacha20_xor(otk, 64, chacha20Cipher->m_key, data + n, counter);
+
+ offset = (page == 1) ? (chacha20Cipher->m_legacy != 0) ? 0 : CIPHER_PAGE1_OFFSET : 0;
+ chacha20_xor(data + offset, n - offset, otk + 32, data + n, counter + 1);
+ if (page == 1)
+ {
+ memcpy(data, chacha20Cipher->m_salt, SALTLENGTH_CHACHA20);
+ }
+ poly1305(data, n + PAGE_NONCE_LEN_CHACHA20, otk, data + n + PAGE_NONCE_LEN_CHACHA20);
+ }
+ else
+ {
+ /* Encrypt only */
+ unsigned char nonce[PAGE_NONCE_LEN_CHACHA20];
+ memset(otk, 0, 64);
+ CodecGenerateInitialVector(page, nonce);
+ counter = LOAD32_LE(&nonce[PAGE_NONCE_LEN_CHACHA20 - 4]) ^ page;
+ chacha20_xor(otk, 64, chacha20Cipher->m_key, nonce, counter);
+
+ /* Encrypt */
+ offset = (page == 1) ? (chacha20Cipher->m_legacy != 0) ? 0 : CIPHER_PAGE1_OFFSET : 0;
+ chacha20_xor(data + offset, n - offset, otk + 32, nonce, counter + 1);
+ if (page == 1)
+ {
+ memcpy(data, chacha20Cipher->m_salt, SALTLENGTH_CHACHA20);
+ }
+ }
+
+ return rc;
+}
+
+int
+DecryptPageChaCha20Cipher(void* cipher, int page, unsigned char* data, int len, int reserved)
+{
+ ChaCha20Cipher* chacha20Cipher = (ChaCha20Cipher*) cipher;
+ int rc = SQLITE_OK;
+ int legacy = chacha20Cipher->m_legacy;
+ int nReserved = (reserved == 0 && legacy == 0) ? 0 : GetReservedChaCha20Cipher(cipher);
+ int n = len - nReserved;
+
+ /* Generate one-time keys */
+ unsigned char otk[64];
+ uint32_t counter;
+ unsigned char tag[16];
+ int offset;
+
+ /* Check whether number of required reserved bytes and actually reserved bytes match */
+ if ((legacy == 0 && nReserved > reserved) || ((legacy != 0 && nReserved != reserved)))
+ {
+ return SQLITE_CORRUPT;
+ }
+
+ if (nReserved > 0)
+ {
+ /* Decrypt and verify MAC */
+ memset(otk, 0, 64);
+ counter = LOAD32_LE(data + n + PAGE_NONCE_LEN_CHACHA20 - 4) ^ page;
+ chacha20_xor(otk, 64, chacha20Cipher->m_key, data + n, counter);
+
+ /* Verify the MAC */
+ poly1305(data, n + PAGE_NONCE_LEN_CHACHA20, otk, tag);
+ if (!poly1305_tagcmp(data + n + PAGE_NONCE_LEN_CHACHA20, tag))
+ {
+ /* Decrypt */
+ offset = (page == 1) ? (chacha20Cipher->m_legacy != 0) ? 0 : CIPHER_PAGE1_OFFSET : 0;
+ chacha20_xor(data + offset, n - offset, otk + 32, data + n, counter + 1);
+ if (page == 1)
+ {
+ memcpy(data, SQLITE_FILE_HEADER, 16);
+ }
+ }
+ else
+ {
+ /* Bad MAC */
+ rc = SQLITE_CORRUPT;
+ }
+ }
+ else
+ {
+ /* Decrypt only */
+ unsigned char nonce[PAGE_NONCE_LEN_CHACHA20];
+ memset(otk, 0, 64);
+ CodecGenerateInitialVector(page, nonce);
+ counter = LOAD32_LE(&nonce[PAGE_NONCE_LEN_CHACHA20 - 4]) ^ page;
+ chacha20_xor(otk, 64, chacha20Cipher->m_key, nonce, counter);
+
+ /* Decrypt */
+ offset = (page == 1) ? (chacha20Cipher->m_legacy != 0) ? 0 : CIPHER_PAGE1_OFFSET : 0;
+ chacha20_xor(data + offset, n - offset, otk + 32, nonce, counter + 1);
+ if (page == 1)
+ {
+ memcpy(data, SQLITE_FILE_HEADER, 16);
+ }
+ }
+
+ return rc;
+}
+
+/* --- SQLCipher AES256CBC-HMAC cipher --- */
+
+#define KEYLENGTH_SQLCIPHER 32
+#define SALTLENGTH_SQLCIPHER 16
+#define HMAC_LENGTH_SQLCIPHER SHA1_DIGEST_SIZE
+#define PAGE_NONCE_LEN_SQLCIPHER 16
+
+typedef struct _sqlCipherCipher
+{
+ int m_legacy;
+ int m_legacyPageSize;
+ int m_kdfIter;
+ int m_fastKdfIter;
+ int m_hmacUse;
+ int m_hmacPgno;
+ int m_hmacSaltMask;
+ int m_keyLength;
+ unsigned char m_key[KEYLENGTH_SQLCIPHER];
+ unsigned char m_salt[SALTLENGTH_SQLCIPHER];
+ unsigned char m_hmacKey[KEYLENGTH_SQLCIPHER];
+ Rijndael* m_aes;
+} SQLCipherCipher;
+
+void*
+AllocateSQLCipherCipher(wx_sqlite3* db)
+{
+ SQLCipherCipher* sqlCipherCipher = (SQLCipherCipher*) wx_sqlite3_malloc(sizeof(SQLCipherCipher));
+ if (sqlCipherCipher != NULL)
+ {
+ sqlCipherCipher->m_aes = (Rijndael*)wx_sqlite3_malloc(sizeof(Rijndael));
+ if (sqlCipherCipher->m_aes != NULL)
+ {
+ sqlCipherCipher->m_keyLength = KEYLENGTH_SQLCIPHER;
+ memset(sqlCipherCipher->m_key, 0, KEYLENGTH_SQLCIPHER);
+ memset(sqlCipherCipher->m_salt, 0, SALTLENGTH_SQLCIPHER);
+ memset(sqlCipherCipher->m_hmacKey, 0, KEYLENGTH_AES256);
+ RijndaelCreate(sqlCipherCipher->m_aes);
+ }
+ else
+ {
+ wx_sqlite3_free(sqlCipherCipher);
+ sqlCipherCipher = NULL;
+ }
+ }
+ if (sqlCipherCipher != NULL)
+ {
+ CipherParams* cipherParams = (CipherParams*) GetCipherParams(db, CODEC_TYPE_SQLCIPHER);
+ sqlCipherCipher->m_legacy = GetCipherParameter(cipherParams, "legacy");
+ sqlCipherCipher->m_legacyPageSize = GetCipherParameter(cipherParams, "legacy_page_size");
+ sqlCipherCipher->m_kdfIter = GetCipherParameter(cipherParams, "kdf_iter");
+ sqlCipherCipher->m_fastKdfIter = GetCipherParameter(cipherParams, "fast_kdf_iter");
+ sqlCipherCipher->m_hmacUse = GetCipherParameter(cipherParams, "hmac_use");
+ sqlCipherCipher->m_hmacPgno = GetCipherParameter(cipherParams, "hmac_pgno");
+ sqlCipherCipher->m_hmacSaltMask = GetCipherParameter(cipherParams, "hmac_salt_mask");
+ }
+ return sqlCipherCipher;
+}
+
+void
+FreeSQLCipherCipher(void* cipher)
+{
+ SQLCipherCipher* sqlCipherCipher = (SQLCipherCipher*) cipher;
+ memset(sqlCipherCipher->m_aes, 0, sizeof(Rijndael));
+ wx_sqlite3_free(sqlCipherCipher->m_aes);
+ memset(sqlCipherCipher, 0, sizeof(SQLCipherCipher));
+ wx_sqlite3_free(sqlCipherCipher);
+}
+
+void
+CloneSQLCipherCipher(void* cipherTo, void* cipherFrom)
+{
+ SQLCipherCipher* sqlCipherCipherTo = (SQLCipherCipher*) cipherTo;
+ SQLCipherCipher* sqlCipherCipherFrom = (SQLCipherCipher*) cipherFrom;
+ sqlCipherCipherTo->m_legacy = sqlCipherCipherFrom->m_legacy;
+ sqlCipherCipherTo->m_legacyPageSize = sqlCipherCipherFrom->m_legacyPageSize;
+ sqlCipherCipherTo->m_kdfIter = sqlCipherCipherFrom->m_kdfIter;
+ sqlCipherCipherTo->m_fastKdfIter = sqlCipherCipherFrom->m_fastKdfIter;
+ sqlCipherCipherTo->m_hmacUse = sqlCipherCipherFrom->m_hmacUse;
+ sqlCipherCipherTo->m_hmacPgno = sqlCipherCipherFrom->m_hmacPgno;
+ sqlCipherCipherTo->m_hmacSaltMask = sqlCipherCipherFrom->m_hmacSaltMask;
+ sqlCipherCipherTo->m_keyLength = sqlCipherCipherFrom->m_keyLength;
+ memcpy(sqlCipherCipherTo->m_key, sqlCipherCipherFrom->m_key, KEYLENGTH_SQLCIPHER);
+ memcpy(sqlCipherCipherTo->m_salt, sqlCipherCipherFrom->m_salt, SALTLENGTH_SQLCIPHER);
+ memcpy(sqlCipherCipherTo->m_hmacKey, sqlCipherCipherFrom->m_hmacKey, KEYLENGTH_SQLCIPHER);
+ RijndaelInvalidate(sqlCipherCipherTo->m_aes);
+ RijndaelInvalidate(sqlCipherCipherFrom->m_aes);
+}
+
+int
+GetLegacySQLCipherCipher(void* cipher)
+{
+ SQLCipherCipher* sqlCipherCipher = (SQLCipherCipher*)cipher;
+ return sqlCipherCipher->m_legacy;
+}
+
+int
+GetPageSizeSQLCipherCipher(void* cipher)
+{
+ SQLCipherCipher* sqlCipherCipher = (SQLCipherCipher*) cipher;
+ int pageSize = 0;
+ if (sqlCipherCipher->m_legacy != 0)
+ {
+ pageSize = sqlCipherCipher->m_legacyPageSize;
+ if ((pageSize < 512) || (pageSize > SQLITE_MAX_PAGE_SIZE) || (((pageSize - 1) & pageSize) != 0))
+ {
+ pageSize = 0;
+ }
+ }
+ return pageSize;
+}
+
+int
+GetReservedSQLCipherCipher(void* cipher)
+{
+ SQLCipherCipher* sqlCipherCipher = (SQLCipherCipher*) cipher;
+ int reserved = SALTLENGTH_SQLCIPHER;
+ if (sqlCipherCipher->m_hmacUse != 0)
+ {
+ reserved += 32;
+ }
+ return reserved;
+}
+
+static int IsHexKey(const unsigned char* hex, int len)
+{
+ int j;
+ for (j = 0; j < len; ++j)
+ {
+ unsigned char c = hex[j];
+ if ((c < '0' || c > '9') && (c < 'A' || c > 'F') && (c < 'a' || c > 'f'))
+ {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int ConvertHex2Int(char c)
+{
+ return (c >= '0' && c <= '9') ? (c)-'0' :
+ (c >= 'A' && c <= 'F') ? (c)-'A' + 10 :
+ (c >= 'a' && c <= 'f') ? (c)-'a' + 10 : 0;
+}
+
+static void ConvertHex2Bin(const unsigned char* hex, int len, unsigned char* bin)
+{
+ int j;
+ for (j = 0; j < len; j += 2)
+ {
+ bin[j / 2] = (ConvertHex2Int(hex[j]) << 4) | ConvertHex2Int(hex[j + 1]);
+ }
+}
+
+void
+GenerateKeySQLCipherCipher(void* cipher, Btree* pBt, char* userPassword, int passwordLength, int rekey)
+{
+ SQLCipherCipher* sqlCipherCipher = (SQLCipherCipher*) cipher;
+
+ Pager *pPager = pBt->pBt->pPager;
+ wx_sqlite3_file* fd = (isOpen(pPager->fd)) ? pPager->fd : NULL;
+
+ if (rekey || fd == NULL || wx_sqlite3OsRead(fd, sqlCipherCipher->m_salt, SALTLENGTH_SQLCIPHER, 0) != SQLITE_OK)
+ {
+ chacha20_rng(sqlCipherCipher->m_salt, SALTLENGTH_SQLCIPHER);
+ }
+
+ if (passwordLength == ((KEYLENGTH_SQLCIPHER * 2) + 3) &&
+ wx_sqlite3_strnicmp(userPassword, "x'", 2) == 0 &&
+ IsHexKey((unsigned char*) (userPassword + 2), KEYLENGTH_SQLCIPHER * 2) != 0)
+ {
+ ConvertHex2Bin((unsigned char*) (userPassword + 2), passwordLength - 3, sqlCipherCipher->m_key);
+ }
+ else if (passwordLength == (((KEYLENGTH_SQLCIPHER + SALTLENGTH_SQLCIPHER) * 2) + 3) &&
+ wx_sqlite3_strnicmp(userPassword, "x'", 2) == 0 &&
+ IsHexKey((unsigned char*) (userPassword + 2), (KEYLENGTH_SQLCIPHER + SALTLENGTH_SQLCIPHER) * 2) != 0)
+ {
+ ConvertHex2Bin((unsigned char*) (userPassword + 2), KEYLENGTH_SQLCIPHER * 2, sqlCipherCipher->m_key);
+ ConvertHex2Bin((unsigned char*) (userPassword + 2 + KEYLENGTH_SQLCIPHER * 2), SALTLENGTH_SQLCIPHER * 2, sqlCipherCipher->m_salt);
+ }
+ else
+ {
+ fastpbkdf2_hmac_sha1((unsigned char*) userPassword, passwordLength,
+ sqlCipherCipher->m_salt, SALTLENGTH_SQLCIPHER,
+ sqlCipherCipher->m_kdfIter,
+ sqlCipherCipher->m_key, KEYLENGTH_SQLCIPHER);
+ }
+
+ if (sqlCipherCipher->m_hmacUse != 0)
+ {
+ int j;
+ unsigned char hmacSaltMask = sqlCipherCipher->m_hmacSaltMask;
+ unsigned char hmacSalt[SALTLENGTH_SQLCIPHER];
+ memcpy(hmacSalt, sqlCipherCipher->m_salt, SALTLENGTH_SQLCIPHER);
+ for (j = 0; j < SALTLENGTH_SQLCIPHER; ++j)
+ {
+ hmacSalt[j] ^= hmacSaltMask;
+ }
+ fastpbkdf2_hmac_sha1(sqlCipherCipher->m_key, KEYLENGTH_SQLCIPHER,
+ hmacSalt, SALTLENGTH_SQLCIPHER,
+ sqlCipherCipher->m_fastKdfIter,
+ sqlCipherCipher->m_hmacKey, KEYLENGTH_SQLCIPHER);
+ }
+}
+
+int
+EncryptPageSQLCipherCipher(void* cipher, int page, unsigned char* data, int len, int reserved)
+{
+ SQLCipherCipher* sqlCipherCipher = (SQLCipherCipher*) cipher;
+ int rc = SQLITE_OK;
+ int legacy = sqlCipherCipher->m_legacy;
+ int nReserved = (reserved == 0 && legacy == 0) ? 0 : GetReservedSQLCipherCipher(cipher);
+ int n = len - nReserved;
+ int offset = (page == 1) ? (sqlCipherCipher->m_legacy != 0) ? 16 : 24 : 0;
+ int blen;
+ unsigned char iv[64];
+
+ /* Check whether number of required reserved bytes and actually reserved bytes match */
+ if ((legacy == 0 && nReserved > reserved) || ((legacy != 0 && nReserved != reserved)))
+ {
+ return SQLITE_CORRUPT;
+ }
+
+ /* Generate nonce (64 bytes) */
+ memset(iv, 0, 64);
+ if (nReserved > 0)
+ {
+ chacha20_rng(iv, 64);
+ }
+ else
+ {
+ CodecGenerateInitialVector(page, iv);
+ }
+
+ RijndaelInit(sqlCipherCipher->m_aes, RIJNDAEL_Direction_Mode_CBC, RIJNDAEL_Direction_Encrypt, sqlCipherCipher->m_key, RIJNDAEL_Direction_KeyLength_Key32Bytes, iv);
+ blen = RijndaelBlockEncrypt(sqlCipherCipher->m_aes, data + offset, (n - offset) * 8, data + offset);
+ if (nReserved > 0)
+ {
+ memcpy(data + n, iv, nReserved);
+ }
+ if (page == 1)
+ {
+ memcpy(data, sqlCipherCipher->m_salt, SALTLENGTH_SQLCIPHER);
+ }
+
+ /* hmac calculation */
+ if (sqlCipherCipher->m_hmacUse == 1 && nReserved > 0)
+ {
+ unsigned char pgno_raw[4];
+ unsigned char hmac_out[64];
+ if (sqlCipherCipher->m_hmacPgno == SQLCIPHER_HMAC_PGNO_LE)
+ {
+ STORE32_LE(pgno_raw, page);
+ }
+ else if (sqlCipherCipher->m_hmacPgno == SQLCIPHER_HMAC_PGNO_BE)
+ {
+ STORE32_BE(pgno_raw, page);
+ }
+ else
+ {
+ memcpy(pgno_raw, &page, 4);
+ }
+ sqlcipher_hmac(sqlCipherCipher->m_hmacKey, KEYLENGTH_SQLCIPHER, data + offset, n + PAGE_NONCE_LEN_SQLCIPHER - offset, pgno_raw, 4, hmac_out);
+ memcpy(data + n + PAGE_NONCE_LEN_SQLCIPHER, hmac_out, HMAC_LENGTH_SQLCIPHER);
+ }
+
+ return rc;
+}
+
+int
+DecryptPageSQLCipherCipher(void* cipher, int page, unsigned char* data, int len, int reserved)
+{
+ SQLCipherCipher* sqlCipherCipher = (SQLCipherCipher*) cipher;
+ int rc = SQLITE_OK;
+ int legacy = sqlCipherCipher->m_legacy;
+ int nReserved = (reserved == 0 && legacy == 0) ? 0 : GetReservedSQLCipherCipher(cipher);
+ int n = len - nReserved;
+ int offset = (page == 1) ? (sqlCipherCipher->m_legacy != 0) ? 16 : 24 : 0;
+ int hmacOk = 1;
+ int blen;
+ unsigned char iv[64];
+
+ /* Check whether number of required reserved bytes and actually reserved bytes match */
+ if ((legacy == 0 && nReserved > reserved) || ((legacy != 0 && nReserved != reserved)))
+ {
+ return SQLITE_CORRUPT;
+ }
+
+ /* Get nonce from buffer */
+ if (nReserved > 0)
+ {
+ memcpy(iv, data + n, nReserved);
+ }
+ else
+ {
+ CodecGenerateInitialVector(page, iv);
+ }
+
+ /* hmac check */
+ if (sqlCipherCipher->m_hmacUse == 1 && nReserved > 0)
+ {
+ unsigned char pgno_raw[4];
+ unsigned char hmac_out[64];
+ if (sqlCipherCipher->m_hmacPgno == SQLCIPHER_HMAC_PGNO_LE)
+ {
+ STORE32_LE(pgno_raw, page);
+ }
+ else if (sqlCipherCipher->m_hmacPgno == SQLCIPHER_HMAC_PGNO_BE)
+ {
+ STORE32_BE(pgno_raw, page);
+ }
+ else
+ {
+ memcpy(pgno_raw, &page, 4);
+ }
+ sqlcipher_hmac(sqlCipherCipher->m_hmacKey, KEYLENGTH_SQLCIPHER, data + offset, n + PAGE_NONCE_LEN_SQLCIPHER - offset, pgno_raw, 4, hmac_out);
+ hmacOk = (memcmp(data + n + PAGE_NONCE_LEN_SQLCIPHER, hmac_out, HMAC_LENGTH_SQLCIPHER) == 0);
+ }
+
+ if (hmacOk != 0)
+ {
+ RijndaelInit(sqlCipherCipher->m_aes, RIJNDAEL_Direction_Mode_CBC, RIJNDAEL_Direction_Decrypt, sqlCipherCipher->m_key, RIJNDAEL_Direction_KeyLength_Key32Bytes, iv);
+ blen = RijndaelBlockDecrypt(sqlCipherCipher->m_aes, data + offset, (n - offset) * 8, data + offset);
+ if (nReserved > 0)
+ {
+ memcpy(data + n, iv, nReserved);
+ }
+ if (page == 1)
+ {
+ memcpy(data, SQLITE_FILE_HEADER, 16);
+ }
+ }
+ else
+ {
+ /* Bad MAC */
+ rc = SQLITE_CORRUPT;
+ }
+
+ return rc;
+}
+
+
+typedef struct _CodecParameter
+{
+ char* m_name;
+ CipherParams* m_params;
+} CodecParameter;
+
+CodecParameter codecParameterTable[] =
+{
+ { "global", commonParams },
+ { "aes128cbc", aes128Params },
+ { "aes256cbc", aes256Params },
+ { "chacha20", chacha20Params },
+ { "sqlcipher", sqlCipherParams },
+ { "", NULL }
+};
+
+CodecParameter*
+CloneCodecParameterTable()
+{
+ /* Count number of codecs and cipher parameters */
+ int nTables = 0;
+ int nParams = 0;
+ int j, k, n;
+ CipherParams* cloneCipherParams;
+ CodecParameter* cloneCodecParams;
+
+ for (j = 0; strlen(codecParameterTable[j].m_name) > 0; ++j)
+ {
+ CipherParams* params = codecParameterTable[j].m_params;
+ for (k = 0; strlen(params[k].m_name) > 0; ++k);
+ nParams += k;
+ }
+ nTables = j;
+
+ /* Allocate memory for cloned codec parameter tables (including sentinel for each table) */
+ cloneCipherParams = (CipherParams*) wx_sqlite3_malloc((nParams + nTables) * sizeof(CipherParams));
+ cloneCodecParams = (CodecParameter*) wx_sqlite3_malloc((nTables + 1) * sizeof(CodecParameter));
+
+ /* Create copy of tables */
+ if (cloneCodecParams != NULL)
+ {
+ int offset = 0;
+ for (j = 0; j < nTables; ++j)
+ {
+ CipherParams* params = codecParameterTable[j].m_params;
+ cloneCodecParams[j].m_name = codecParameterTable[j].m_name;
+ cloneCodecParams[j].m_params = &cloneCipherParams[offset];
+ for (n = 0; strlen(params[n].m_name) > 0; ++n);
+ /* Copy all parameters of the current table (including sentinel) */
+ for (k = 0; k <= n; ++k)
+ {
+ cloneCipherParams[offset + k].m_name = params[k].m_name;
+ cloneCipherParams[offset + k].m_value = params[k].m_value;
+ cloneCipherParams[offset + k].m_default = params[k].m_default;
+ cloneCipherParams[offset + k].m_minValue = params[k].m_minValue;
+ cloneCipherParams[offset + k].m_maxValue = params[k].m_maxValue;
+ }
+ offset += (n + 1);
+ }
+ cloneCodecParams[nTables].m_name = codecParameterTable[nTables].m_name;
+ cloneCodecParams[nTables].m_params = NULL;
+ }
+ else
+ {
+ wx_sqlite3_free(cloneCipherParams);
+ }
+ return cloneCodecParams;
+}
+
+void
+FreeCodecParameterTable(CodecParameter* codecParams)
+{
+ wx_sqlite3_free(codecParams[0].m_params);
+ wx_sqlite3_free(codecParams);
+}
+
+typedef void* (*AllocateCipher_t)(wx_sqlite3* db);
+typedef void (*FreeCipher_t)(void* cipher);
+typedef void (*CloneCipher_t)(void* cipherTo, void* cipherFrom);
+typedef int (*GetLegacy_t)(void* cipher);
+typedef int (*GetPageSize_t)(void* cipher);
+typedef int (*GetReserved_t)(void* cipher);
+typedef void (*GenerateKey_t)(void* cipher, Btree* pBt, char* userPassword, int passwordLength, int rekey);
+typedef int (*EncryptPage_t)(void* cipher, int page, unsigned char* data, int len, int reserved);
+typedef int (*DecryptPage_t)(void* cipher, int page, unsigned char* data, int len, int reserved);
+
+typedef struct _CodecDescriptor
+{
+ char m_name[32];
+ AllocateCipher_t m_allocateCipher;
+ FreeCipher_t m_freeCipher;
+ CloneCipher_t m_cloneCipher;
+ GetLegacy_t m_getLegacy;
+ GetPageSize_t m_getPageSize;
+ GetReserved_t m_getReserved;
+ GenerateKey_t m_generateKey;
+ EncryptPage_t m_encryptPage;
+ DecryptPage_t m_decryptPage;
+} CodecDescriptor;
+
+CodecDescriptor codecDescriptorTable[] =
+{
+ /* wxSQLite3 AES 128 bit CBC */
+ { "aes128cbc", AllocateAES128Cipher,
+ FreeAES128Cipher,
+ CloneAES128Cipher,
+ GetLegacyAES128Cipher,
+ GetPageSizeAES128Cipher,
+ GetReservedAES128Cipher,
+ GenerateKeyAES128Cipher,
+ EncryptPageAES128Cipher,
+ DecryptPageAES128Cipher },
+ /* wxSQLite3 AES 128 bit CBC */
+ { "aes256cbc", AllocateAES256Cipher,
+ FreeAES256Cipher,
+ CloneAES256Cipher,
+ GetLegacyAES256Cipher,
+ GetPageSizeAES256Cipher,
+ GetReservedAES256Cipher,
+ GenerateKeyAES256Cipher,
+ EncryptPageAES256Cipher,
+ DecryptPageAES256Cipher },
+ /* ChaCha20 - Poly1305 (including sqleet legacy */
+ { "chacha20", AllocateChaCha20Cipher,
+ FreeChaCha20Cipher,
+ CloneChaCha20Cipher,
+ GetLegacyChaCha20Cipher,
+ GetPageSizeChaCha20Cipher,
+ GetReservedChaCha20Cipher,
+ GenerateKeyChaCha20Cipher,
+ EncryptPageChaCha20Cipher,
+ DecryptPageChaCha20Cipher },
+ /* ChaCha20 - Poly1305 (including sqleet legacy */
+ { "sqlcipher", AllocateSQLCipherCipher,
+ FreeSQLCipherCipher,
+ CloneSQLCipherCipher,
+ GetLegacySQLCipherCipher,
+ GetPageSizeSQLCipherCipher,
+ GetReservedSQLCipherCipher,
+ GenerateKeySQLCipherCipher,
+ EncryptPageSQLCipherCipher,
+ DecryptPageSQLCipherCipher },
+ { "", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+};
+
+/* --- Codec --- */
+
+void
+wxwx_sqlite3_config_table(wx_sqlite3_context* context, int argc, wx_sqlite3_value** argv)
+{
+ CodecParameter* codecParams = (CodecParameter*) wx_sqlite3_user_data(context);
+ assert(argc == 0);
+ wx_sqlite3_result_pointer(context, codecParams, "wxwx_sqlite3_codec_params", 0);
+}
+
+/*
+** smallest integer value not less than argument
+*/
+void
+wxwx_sqlite3_config_params(wx_sqlite3_context* context, int argc, wx_sqlite3_value** argv)
+{
+ CodecParameter* codecParams;
+ const char* nameParam1;
+ int hasDefaultPrefix = 0;
+ int hasMinPrefix = 0;
+ int hasMaxPrefix = 0;
+ CipherParams* param1;
+ CipherParams* cipherParamTable = NULL;
+ int isCommonParam1;
+ int isCipherParam1 = 0;
+
+ assert(argc == 1 || argc == 2 || argc == 3);
+ /* NULL values are not allowed for the first 2 arguments */
+ if (SQLITE_NULL == wx_sqlite3_value_type(argv[0]) || (argc > 1 && SQLITE_NULL == wx_sqlite3_value_type(argv[1])))
+ {
+ wx_sqlite3_result_null(context);
+ return;
+ }
+
+ codecParams = (CodecParameter*) wx_sqlite3_user_data(context);
+
+ /* Check first argument whether it is a common parameter */
+ /* If the first argument is a common parameter, param1 will point to its parameter table entry */
+ nameParam1 = (const char*) wx_sqlite3_value_text(argv[0]);
+ if (wx_sqlite3_strnicmp(nameParam1, "default:", 8) == 0)
+ {
+ hasDefaultPrefix = 1;
+ nameParam1 += 8;
+ }
+ if (wx_sqlite3_strnicmp(nameParam1, "min:", 4) == 0)
+ {
+ hasMinPrefix = 1;
+ nameParam1 += 4;
+ }
+ if (wx_sqlite3_strnicmp(nameParam1, "max:", 4) == 0)
+ {
+ hasMaxPrefix = 1;
+ nameParam1 += 4;
+ }
+
+ param1 = codecParams[0].m_params;
+ cipherParamTable = NULL;
+ for (; strlen(param1->m_name) > 0; ++param1)
+ {
+ if (wx_sqlite3_stricmp(nameParam1, param1->m_name) == 0) break;
+ }
+ isCommonParam1 = strlen(param1->m_name) > 0;
+
+ /* Check first argument whether it is a cipher name, if it wasn't a common parameter */
+ /* If the first argument is a cipher name, cipherParamTable will point to the corresponding cipher parameter table */
+ if (!isCommonParam1)
+ {
+ if (!hasDefaultPrefix && !hasMinPrefix && !hasMaxPrefix)
+ {
+ int j = 0;
+ for (j = 0; strlen(codecParams[j].m_name) > 0; ++j)
+ {
+ if (wx_sqlite3_stricmp(nameParam1, codecParams[j].m_name) == 0) break;
+ }
+ isCipherParam1 = strlen(codecParams[j].m_name) > 0;
+ if (isCipherParam1)
+ {
+ cipherParamTable = codecParams[j].m_params;
+ }
+ }
+ if (!isCipherParam1)
+ {
+ /* Prefix not allowed for cipher names or cipher name not found */
+ wx_sqlite3_result_null(context);
+ return;
+ }
+ }
+
+ if (argc == 1)
+ {
+ /* Return value of param1 */
+ if (isCommonParam1)
+ {
+ int value = (hasDefaultPrefix) ? param1->m_default : (hasMinPrefix) ? param1->m_minValue : (hasMaxPrefix) ? param1->m_maxValue : param1->m_value;
+ if (wx_sqlite3_stricmp(nameParam1, "cipher") == 0)
+ {
+ wx_sqlite3_result_text(context, codecDescriptorTable[value-1].m_name, -1, SQLITE_STATIC);
+ }
+ else
+ {
+ wx_sqlite3_result_int(context, value);
+ }
+ }
+ else if (isCipherParam1)
+ {
+ /* Return a list of available parameters for the requested cipher */
+ int nParams = 0;
+ int lenTotal = 0;
+ int j;
+ for (j = 0; strlen(cipherParamTable[j].m_name) > 0; ++j)
+ {
+ ++nParams;
+ lenTotal += strlen(cipherParamTable[j].m_name);
+ }
+ if (nParams > 0)
+ {
+ char* paramList = (char*) wx_sqlite3_malloc(lenTotal + nParams);
+ if (paramList != NULL)
+ {
+ char* p = paramList;
+ strcpy(paramList, cipherParamTable[0].m_name);
+ for (j = 1; j < nParams; ++j)
+ {
+ strcat(paramList, ",");
+ strcat(paramList, cipherParamTable[j].m_name);
+ }
+ wx_sqlite3_result_text(context, paramList, -1, wx_sqlite3_free);
+ }
+ else
+ {
+ /* Not enough memory to allocate the result */
+ wx_sqlite3_result_error_nomem(context);
+ }
+ }
+ else
+ {
+ /* Cipher has no parameters */
+ wx_sqlite3_result_null(context);
+ }
+ }
+ }
+ else
+ {
+ int arg2Type = wx_sqlite3_value_type(argv[1]);
+ if (argc == 2 && isCommonParam1)
+ {
+ /* Set value of common parameter */
+ if (wx_sqlite3_stricmp(nameParam1, "cipher") == 0)
+ {
+ /* 2nd argument is a cipher name */
+ if (arg2Type == SQLITE_TEXT)
+ {
+ const char* nameCipher = (const char*) wx_sqlite3_value_text(argv[1]);
+ int j = 0;
+ for (j = 0; strlen(codecDescriptorTable[j].m_name) > 0; ++j)
+ {
+ if (wx_sqlite3_stricmp(nameCipher, codecDescriptorTable[j].m_name) == 0) break;
+ }
+ if (strlen(codecDescriptorTable[j].m_name) > 0)
+ {
+ if (hasDefaultPrefix)
+ {
+ param1->m_default = j + 1;
+ }
+ param1->m_value = j + 1;
+ wx_sqlite3_result_text(context, codecDescriptorTable[j].m_name, -1, SQLITE_STATIC);
+ }
+ else
+ {
+ /* No match for cipher name found */
+ wx_sqlite3_result_null(context);
+ }
+ }
+ else
+ {
+ /* Invalid parameter type */
+ wx_sqlite3_result_null(context);
+ }
+ }
+ else if (arg2Type == SQLITE_INTEGER)
+ {
+ /* Check that parameter value is within allowed range */
+ int value = wx_sqlite3_value_int(argv[1]);
+ if (value >= param1->m_minValue && value <= param1->m_maxValue)
+ {
+ if (hasDefaultPrefix)
+ {
+ param1->m_default = value;
+ }
+ param1->m_value = value;
+ wx_sqlite3_result_int(context, value);
+ }
+ else
+ {
+ /* Parameter value not within allowed range */
+ wx_sqlite3_result_null(context);
+ }
+ }
+ else
+ {
+ wx_sqlite3_result_null(context);
+ }
+ }
+ else if (isCipherParam1 && arg2Type == SQLITE_TEXT)
+ {
+ const char* nameParam2 = (const char*) wx_sqlite3_value_text(argv[1]);
+ CipherParams* param2 = cipherParamTable;
+ hasDefaultPrefix = 0;
+ if (wx_sqlite3_strnicmp(nameParam2, "default:", 8) == 0)
+ {
+ hasDefaultPrefix = 1;
+ nameParam2 += 8;
+ }
+ hasMinPrefix = 0;
+ if (wx_sqlite3_strnicmp(nameParam2, "min:", 4) == 0)
+ {
+ hasMinPrefix = 1;
+ nameParam2 += 4;
+ }
+ hasMaxPrefix = 0;
+ if (wx_sqlite3_strnicmp(nameParam2, "max:", 4) == 0)
+ {
+ hasMaxPrefix = 1;
+ nameParam2 += 4;
+ }
+ for (; strlen(param2->m_name) > 0; ++param2)
+ {
+ if (wx_sqlite3_stricmp(nameParam2, param2->m_name) == 0) break;
+ }
+ if (strlen(param2->m_name) > 0)
+ {
+ if (argc == 2)
+ {
+ /* Return parameter value */
+ int value = (hasDefaultPrefix) ? param2->m_default : (hasMinPrefix) ? param2->m_minValue : (hasMaxPrefix) ? param2->m_maxValue : param2->m_value;
+ wx_sqlite3_result_int(context, value);
+ }
+ else if (!hasMinPrefix && !hasMaxPrefix && wx_sqlite3_value_type(argv[2]) == SQLITE_INTEGER)
+ {
+ /* Change cipher parameter value */
+ int value = wx_sqlite3_value_int(argv[2]);
+ if (value >= param2->m_minValue && value <= param2->m_maxValue)
+ {
+ if (hasDefaultPrefix)
+ {
+ param2->m_default = value;
+ }
+ param2->m_value = value;
+ wx_sqlite3_result_int(context, value);
+ }
+ else
+ {
+ /* Cipher parameter value not within allowed range */
+ wx_sqlite3_result_null(context);
+ }
+ }
+ else
+ {
+ /* Only current value and default value of a parameter can be changed */
+ wx_sqlite3_result_null(context);
+ }
+ }
+ else
+ {
+ /* Cipher parameter not found */
+ wx_sqlite3_result_null(context);
+ }
+ }
+ else
+ {
+ /* Cipher has no parameters */
+ wx_sqlite3_result_null(context);
+ }
+ }
+}
+
+CodecParameter*
+GetCodecParams(wx_sqlite3* db)
+{
+#if 0
+ wx_sqlite3_mutex_enter(db->mutex);
+#endif
+ CodecParameter* codecParams = NULL;
+ wx_sqlite3_stmt* pStmt = 0;
+ int rc = wx_sqlite3_prepare_v2(db, "SELECT wxwx_sqlite3_config_table();", -1, &pStmt, 0);
+ if (rc == SQLITE_OK)
+ {
+ if (SQLITE_ROW == wx_sqlite3_step(pStmt))
+ {
+ wx_sqlite3_value* ptrValue = wx_sqlite3_column_value(pStmt, 0);
+ codecParams = (CodecParameter*) wx_sqlite3_value_pointer(ptrValue, "wxwx_sqlite3_codec_params");
+ }
+ wx_sqlite3_finalize(pStmt);
+ }
+#if 0
+ wx_sqlite3_mutex_leave(db->mutex);
+#endif
+ return codecParams;
+}
+
+int
+wxwx_sqlite3_config(wx_sqlite3* db, const char* paramName, int newValue)
+{
+ int value = -1;
+ CodecParameter* codecParams;
+ int hasDefaultPrefix = 0;
+ int hasMinPrefix = 0;
+ int hasMaxPrefix = 0;
+ CipherParams* param;
+
+ if (paramName == NULL || (db == NULL && newValue >= 0))
+ {
+ return value;
+ }
+
+ codecParams = (db != NULL) ? GetCodecParams(db) : codecParameterTable;
+ if (codecParams == NULL)
+ {
+ return value;
+ }
+
+ if (wx_sqlite3_strnicmp(paramName, "default:", 8) == 0)
+ {
+ hasDefaultPrefix = 1;
+ paramName += 8;
+ }
+ if (wx_sqlite3_strnicmp(paramName, "min:", 4) == 0)
+ {
+ hasMinPrefix = 1;
+ paramName += 4;
+ }
+ if (wx_sqlite3_strnicmp(paramName, "max:", 4) == 0)
+ {
+ hasMaxPrefix = 1;
+ paramName += 4;
+ }
+
+ param = codecParams[0].m_params;
+ for (; strlen(param->m_name) > 0; ++param)
+ {
+ if (wx_sqlite3_stricmp(paramName, param->m_name) == 0) break;
+ }
+ if (strlen(param->m_name) > 0)
+ {
+ if (db != NULL)
+ {
+ wx_sqlite3_mutex_enter(db->mutex);
+ }
+ else
+ {
+ wx_sqlite3_mutex_enter(wx_sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER));
+ }
+ value = (hasDefaultPrefix) ? param->m_default : (hasMinPrefix) ? param->m_minValue : (hasMaxPrefix) ? param->m_maxValue : param->m_value;
+ if (!hasMinPrefix && !hasMaxPrefix && newValue >= 0 && newValue >= param->m_minValue && newValue <= param->m_maxValue)
+ {
+ if (hasDefaultPrefix)
+ {
+ param->m_default = newValue;
+ }
+ param->m_value = newValue;
+ value = newValue;
+ }
+ if (db != NULL)
+ {
+ wx_sqlite3_mutex_leave(db->mutex);
+ }
+ else
+ {
+ wx_sqlite3_mutex_leave(wx_sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER));
+ }
+ }
+ return value;
+}
+
+int
+wxwx_sqlite3_config_cipher(wx_sqlite3* db, const char* cipherName, const char* paramName, int newValue)
+{
+ int value = -1;
+ CodecParameter* codecParams;
+ CipherParams* cipherParamTable = NULL;
+ int j = 0;
+
+ if (cipherName == NULL || paramName == NULL || (db == NULL && newValue >= 0))
+ {
+ return value;
+ }
+
+ codecParams = (db != NULL) ? GetCodecParams(db) : codecParameterTable;
+ if (codecParams == NULL)
+ {
+ return value;
+ }
+
+ for (j = 0; strlen(codecParams[j].m_name) > 0; ++j)
+ {
+ if (wx_sqlite3_stricmp(cipherName, codecParams[j].m_name) == 0) break;
+ }
+ if (strlen(codecParams[j].m_name) > 0)
+ {
+ cipherParamTable = codecParams[j].m_params;
+ }
+
+ if (cipherParamTable != NULL)
+ {
+ int hasDefaultPrefix = 0;
+ int hasMinPrefix = 0;
+ int hasMaxPrefix = 0;
+ CipherParams* param = cipherParamTable;
+
+ if (wx_sqlite3_strnicmp(paramName, "default:", 8) == 0)
+ {
+ hasDefaultPrefix = 1;
+ paramName += 8;
+ }
+ if (wx_sqlite3_strnicmp(paramName, "min:", 4) == 0)
+ {
+ hasMinPrefix = 1;
+ paramName += 4;
+ }
+ if (wx_sqlite3_strnicmp(paramName, "max:", 4) == 0)
+ {
+ hasMaxPrefix = 1;
+ paramName += 4;
+ }
+
+ for (; strlen(param->m_name) > 0; ++param)
+ {
+ if (wx_sqlite3_stricmp(paramName, param->m_name) == 0) break;
+ }
+ if (strlen(param->m_name) > 0)
+ {
+ if (db != NULL)
+ {
+ wx_sqlite3_mutex_enter(db->mutex);
+ }
+ else
+ {
+ wx_sqlite3_mutex_enter(wx_sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER));
+ }
+ value = (hasDefaultPrefix) ? param->m_default : (hasMinPrefix) ? param->m_minValue : (hasMaxPrefix) ? param->m_maxValue : param->m_value;
+ if (!hasMinPrefix && !hasMaxPrefix && newValue >= 0 && newValue >= param->m_minValue && newValue <= param->m_maxValue)
+ {
+ if (hasDefaultPrefix)
+ {
+ param->m_default = newValue;
+ }
+ param->m_value = newValue;
+ value = newValue;
+ }
+ if (db != NULL)
+ {
+ wx_sqlite3_mutex_leave(db->mutex);
+ }
+ else
+ {
+ wx_sqlite3_mutex_leave(wx_sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER));
+ }
+ }
+ }
+ return value;
+}
+
+int
+GetCipherType(wx_sqlite3* db)
+{
+ CodecParameter* codecParams = (db != NULL) ? GetCodecParams(db) : codecParameterTable;
+ CipherParams* cipherParamTable = (codecParams != NULL) ? codecParams[0].m_params : commonParams;
+#if 0
+ if (codecParams == NULL)
+ {
+ return value;
+ }
+
+ int j = 0;
+ for (j = 0; strlen(codecParams[j].m_name) > 0; ++j)
+ {
+ if (wx_sqlite3_stricmp(cipherName, codecParams[j].m_name) == 0) break;
+ }
+ if (strlen(codecParams[j].m_name) > 0)
+ {
+ cipherParamTable = codecParams[j].m_params;
+ }
+#endif
+
+ int cipherType = CODEC_TYPE;
+ CipherParams* cipher = cipherParamTable;
+ for (; strlen(cipher->m_name) > 0; ++cipher)
+ {
+ if (wx_sqlite3_stricmp("cipher", cipher->m_name) == 0) break;
+ }
+ if (strlen(cipher->m_name) > 0)
+ {
+ cipherType = cipher->m_value;
+ cipher->m_value = cipher->m_default;
+ }
+ return cipherType;
+}
+
+void*
+GetCipherParams(wx_sqlite3* db, int cypherType)
+{
+ CodecParameter* codecParams = (db != NULL) ? GetCodecParams(db) : codecParameterTable;
+ CipherParams* cipherParamTable = (codecParams != NULL) ? codecParams[cypherType].m_params : codecParameterTable[cypherType].m_params;
+ return cipherParamTable;
+}
+
+int
+CodecInit(Codec* codec)
+{
+ int rc = SQLITE_OK;
+ if (codec != NULL)
+ {
+ codec->m_isEncrypted = 0;
+ codec->m_hasReadCipher = 0;
+ codec->m_readCipherType = CODEC_TYPE_UNKNOWN;
+ codec->m_readCipher = NULL;
+ codec->m_hasWriteCipher = 0;
+ codec->m_writeCipherType = CODEC_TYPE_UNKNOWN;
+ codec->m_writeCipher = NULL;
+ codec->m_db = NULL;
+ codec->m_bt = NULL;
+ memset(codec->m_page, 0, sizeof(codec->m_page));
+ codec->m_pageSize = 0;
+ codec->m_reserved = 0;
+ }
+ else
+ {
+ rc = SQLITE_NOMEM;
+ }
+ return rc;
+}
+
+void
+CodecTerm(Codec* codec)
+{
+ if (codec->m_readCipher != NULL)
+ {
+ codecDescriptorTable[codec->m_readCipherType-1].m_freeCipher(codec->m_readCipher);
+ codec->m_readCipher = NULL;
+ }
+ if (codec->m_writeCipher != NULL)
+ {
+ codecDescriptorTable[codec->m_writeCipherType-1].m_freeCipher(codec->m_writeCipher);
+ codec->m_writeCipher = NULL;
+ }
+ memset(codec, 0, sizeof(Codec));
+}
+
+int
+CodecSetup(Codec* codec, int cipherType, char* userPassword, int passwordLength)
+{
+ int rc = SQLITE_OK;
+ codec->m_isEncrypted = 1;
+ codec->m_hasReadCipher = 1;
+ codec->m_hasWriteCipher = 1;
+ codec->m_readCipherType = cipherType;
+ codec->m_readCipher = codecDescriptorTable[codec->m_readCipherType-1].m_allocateCipher(codec->m_db);
+ if (codec->m_readCipher != NULL)
+ {
+ CodecGenerateReadKey(codec, userPassword, passwordLength);
+ rc = CodecCopyCipher(codec, 1);
+ }
+ else
+ {
+ rc = SQLITE_NOMEM;
+ }
+ return rc;
+}
+
+int
+CodecSetupWriteCipher(Codec* codec, int cipherType, char* userPassword, int passwordLength)
+{
+ int rc = SQLITE_OK;
+ if (codec->m_writeCipher != NULL)
+ {
+ codecDescriptorTable[codec->m_writeCipherType-1].m_freeCipher(codec->m_writeCipher);
+ }
+ codec->m_isEncrypted = 1;
+ codec->m_hasWriteCipher = 1;
+ codec->m_writeCipherType = cipherType;
+ codec->m_writeCipher = codecDescriptorTable[codec->m_writeCipherType-1].m_allocateCipher(codec->m_db);
+ if (codec->m_writeCipher != NULL)
+ {
+ CodecGenerateWriteKey(codec, userPassword, passwordLength);
+ }
+ else
+ {
+ rc = SQLITE_NOMEM;
+ }
+ return rc;
+}
+
+void
+CodecSetIsEncrypted(Codec* codec, int isEncrypted)
+{
+ codec->m_isEncrypted = isEncrypted;
+}
+
+void
+CodecSetReadCipherType(Codec* codec, int cipherType)
+{
+ codec->m_readCipherType = cipherType;
+}
+
+void
+CodecSetWriteCipherType(Codec* codec, int cipherType)
+{
+ codec->m_writeCipherType = cipherType;
+}
+
+void
+CodecSetHasReadCipher(Codec* codec, int hasReadCipher)
+{
+ codec->m_hasReadCipher = hasReadCipher;
+}
+
+void
+CodecSetHasWriteCipher(Codec* codec, int hasWriteCipher)
+{
+ codec->m_hasWriteCipher = hasWriteCipher;
+}
+
+void
+CodecSetDb(Codec* codec, wx_sqlite3* db)
+{
+ codec->m_db = db;
+}
+
+void
+CodecSetBtree(Codec* codec, Btree* bt)
+{
+ codec->m_bt = bt;
+}
+
+int
+CodecIsEncrypted(Codec* codec)
+{
+ return codec->m_isEncrypted;
+}
+
+int
+CodecHasReadCipher(Codec* codec)
+{
+ return codec->m_hasReadCipher;
+}
+
+int
+CodecHasWriteCipher(Codec* codec)
+{
+ return codec->m_hasWriteCipher;
+}
+
+Btree*
+CodecGetBtree(Codec* codec)
+{
+ return codec->m_bt;
+}
+
+unsigned char*
+CodecGetPageBuffer(Codec* codec)
+{
+ return &codec->m_page[4];
+}
+
+int CodecGetLegacyReadCipher(Codec* codec)
+{
+ int legacy = (codec->m_hasReadCipher && codec->m_readCipher != NULL) ? codecDescriptorTable[codec->m_readCipherType - 1].m_getLegacy(codec->m_readCipher) : 0;
+ return legacy;
+}
+
+int CodecGetLegacyWriteCipher(Codec* codec)
+{
+ int legacy = (codec->m_hasWriteCipher && codec->m_writeCipher != NULL) ? codecDescriptorTable[codec->m_writeCipherType - 1].m_getLegacy(codec->m_writeCipher) : -1;
+ return legacy;
+}
+
+int
+CodecGetPageSizeReadCipher(Codec* codec)
+{
+ int pageSize = (codec->m_hasReadCipher && codec->m_readCipher != NULL) ? codecDescriptorTable[codec->m_readCipherType - 1].m_getPageSize(codec->m_readCipher) : 0;
+ return pageSize;
+}
+
+int
+CodecGetPageSizeWriteCipher(Codec* codec)
+{
+ int pageSize = (codec->m_hasWriteCipher && codec->m_writeCipher != NULL) ? codecDescriptorTable[codec->m_writeCipherType - 1].m_getPageSize(codec->m_writeCipher) : -1;
+ return pageSize;
+}
+
+int
+CodecGetReservedReadCipher(Codec* codec)
+{
+ int reserved = (codec->m_hasReadCipher && codec->m_readCipher != NULL) ? codecDescriptorTable[codec->m_readCipherType-1].m_getReserved(codec->m_readCipher) : -1;
+ return reserved;
+}
+
+int
+CodecGetReservedWriteCipher(Codec* codec)
+{
+ int reserved = (codec->m_hasWriteCipher && codec->m_writeCipher != NULL) ? codecDescriptorTable[codec->m_writeCipherType-1].m_getReserved(codec->m_writeCipher) : -1;
+ return reserved;
+}
+
+int
+CodecReservedEqual(Codec* codec)
+{
+ int readReserved = (codec->m_hasReadCipher && codec->m_readCipher != NULL) ? codecDescriptorTable[codec->m_readCipherType-1].m_getReserved(codec->m_readCipher) : -1;
+ int writeReserved = (codec->m_hasWriteCipher && codec->m_writeCipher != NULL) ? codecDescriptorTable[codec->m_writeCipherType-1].m_getReserved(codec->m_writeCipher) : -1;
+ return (readReserved == writeReserved);
+}
+
+int
+CodecCopy(Codec* codec, Codec* other)
+{
+ int rc = SQLITE_OK;
+ codec->m_isEncrypted = other->m_isEncrypted;
+ codec->m_hasReadCipher = other->m_hasReadCipher;
+ codec->m_hasWriteCipher = other->m_hasWriteCipher;
+ codec->m_readCipherType = other->m_readCipherType;
+ codec->m_writeCipherType = other->m_writeCipherType;
+ codec->m_readCipher = NULL;
+ codec->m_writeCipher = NULL;
+
+ if (codec->m_hasReadCipher)
+ {
+ codec->m_readCipher = codecDescriptorTable[codec->m_readCipherType - 1].m_allocateCipher(codec->m_db);
+ if (codec->m_readCipher != NULL)
+ {
+ codecDescriptorTable[codec->m_readCipherType - 1].m_cloneCipher(codec->m_readCipher, other->m_readCipher);
+ }
+ else
+ {
+ rc = SQLITE_NOMEM;
+ }
+ }
+
+ if (codec->m_hasWriteCipher)
+ {
+ codec->m_writeCipher = codecDescriptorTable[codec->m_writeCipherType - 1].m_allocateCipher(codec->m_db);
+ if (codec->m_writeCipher != NULL)
+ {
+ codecDescriptorTable[codec->m_writeCipherType - 1].m_cloneCipher(codec->m_writeCipher, other->m_writeCipher);
+ }
+ else
+ {
+ rc = SQLITE_NOMEM;
+ }
+ }
+ codec->m_db = other->m_db;
+ codec->m_bt = other->m_bt;
+ return rc;
+}
+
+int
+CodecCopyCipher(Codec* codec, int read2write)
+{
+ int rc = SQLITE_OK;
+ if (read2write)
+ {
+ if (codec->m_writeCipher != NULL && codec->m_writeCipherType != codec->m_readCipherType)
+ {
+ codecDescriptorTable[codec->m_writeCipherType-1].m_freeCipher(codec->m_writeCipher);
+ codec->m_writeCipher = NULL;
+ }
+ if (codec->m_writeCipher == NULL)
+ {
+ codec->m_writeCipherType = codec->m_readCipherType;
+ codec->m_writeCipher = codecDescriptorTable[codec->m_writeCipherType-1].m_allocateCipher(codec->m_db);
+ }
+ if (codec->m_writeCipher != NULL)
+ {
+ codecDescriptorTable[codec->m_writeCipherType-1].m_cloneCipher(codec->m_writeCipher, codec->m_readCipher);
+ }
+ else
+ {
+ rc = SQLITE_NOMEM;
+ }
+ }
+ else
+ {
+ if (codec->m_readCipher != NULL && codec->m_readCipherType != codec->m_writeCipherType)
+ {
+ codecDescriptorTable[codec->m_readCipherType-1].m_freeCipher(codec->m_readCipher);
+ codec->m_readCipher = NULL;
+ }
+ if (codec->m_readCipher == NULL)
+ {
+ codec->m_readCipherType = codec->m_writeCipherType;
+ codec->m_readCipher = codecDescriptorTable[codec->m_readCipherType-1].m_allocateCipher(codec->m_db);
+ }
+ if (codec->m_readCipher != NULL)
+ {
+ codecDescriptorTable[codec->m_readCipherType-1].m_cloneCipher(codec->m_readCipher, codec->m_writeCipher);
+ }
+ else
+ {
+ rc = SQLITE_NOMEM;
+ }
+ }
+ return rc;
+}
+
+void
+CodecPadPassword(char* password, int pswdlen, unsigned char pswd[32])
+{
+ int j;
+ int p = 0;
+ int m = pswdlen;
+ if (m > 32) m = 32;
+
+ for (j = 0; j < m; j++)
+ {
+ pswd[p++] = (unsigned char) password[j];
+ }
+ for (j = 0; p < 32 && j < 32; j++)
+ {
+ pswd[p++] = padding[j];
+ }
+}
+
+void
+CodecGenerateReadKey(Codec* codec, char* userPassword, int passwordLength)
+{
+ codecDescriptorTable[codec->m_readCipherType-1].m_generateKey(codec->m_readCipher, codec->m_bt, userPassword, passwordLength, 0);
+}
+
+void
+CodecGenerateWriteKey(Codec* codec, char* userPassword, int passwordLength)
+{
+ codecDescriptorTable[codec->m_writeCipherType-1].m_generateKey(codec->m_writeCipher, codec->m_bt, userPassword, passwordLength, 1);
+}
+
+int
+CodecEncrypt(Codec* codec, int page, unsigned char* data, int len, int useWriteKey)
+{
+ int cipherType = (useWriteKey) ? codec->m_writeCipherType : codec->m_readCipherType;
+ void* cipher = (useWriteKey) ? codec->m_writeCipher : codec->m_readCipher;
+ return codecDescriptorTable[cipherType-1].m_encryptPage(cipher, page, data, len, codec->m_reserved);
+}
+
+int
+CodecDecrypt(Codec* codec, int page, unsigned char* data, int len)
+{
+ int cipherType = codec->m_readCipherType;
+ void* cipher = codec->m_readCipher;
+ return codecDescriptorTable[cipherType-1].m_decryptPage(cipher, page, data, len, codec->m_reserved);
+}
+