diff options
Diffstat (limited to 'src/wrap/sqlite')
-rw-r--r-- | src/wrap/sqlite/codec.cpp | 208 | ||||
-rw-r--r-- | src/wrap/sqlite/codec.h | 51 | ||||
-rw-r--r-- | src/wrap/sqlite/codecext.c | 242 | ||||
-rw-r--r-- | src/wrap/sqlite/codecext.cpp | 261 | ||||
-rw-r--r-- | src/wrap/sqlite/readme.txt | 33 | ||||
-rw-r--r-- | src/wrap/sqlite/sqlite.diff | 77 | ||||
-rw-r--r-- | src/wrap/sqlite/sqlite3.diff | 46 | ||||
-rw-r--r-- | src/wrap/sqlite/test_sqlite.cpp | 2 |
8 files changed, 463 insertions, 457 deletions
diff --git a/src/wrap/sqlite/codec.cpp b/src/wrap/sqlite/codec.cpp index 60c8f6a21..9799b058f 100644 --- a/src/wrap/sqlite/codec.cpp +++ b/src/wrap/sqlite/codec.cpp @@ -13,111 +13,191 @@ Codec::Codec(void *db) InitializeCodec(db); } -Codec::Codec(const Codec& other, void *db) +Codec::Codec(const Codec *other, void *db) { //Only used to copy main db key for an attached db InitializeCodec(db); - m_hasReadKey = other.m_hasReadKey; - m_hasWriteKey = other.m_hasWriteKey; - m_readKey = other.m_readKey; - m_ivReadKey = other.m_ivReadKey; - m_writeKey = other.m_writeKey; - m_ivWriteKey = other.m_ivWriteKey; + m_hasReadKey = other->m_hasReadKey; + m_hasWriteKey = other->m_hasWriteKey; + m_readKey = other->m_readKey; + m_ivReadKey = other->m_ivReadKey; + m_writeKey = other->m_writeKey; + m_ivWriteKey = other->m_ivWriteKey; } -void -Codec::InitializeCodec(void *db) +void Codec::InitializeCodec(void *db) { - bool botanInitialized = false; - Library_State* state = swap_global_state(0); - if(state) - { - botanInitialized = true; - swap_global_state(state); // should return NULL FIXME: what if not? - } - - if (!botanInitialized) - LibraryInitializer::initialize(); - m_hasReadKey = false; m_hasWriteKey = false; m_db = db; - m_encipherFilter = get_cipher(BLOCK_CIPHER_STR, ENCRYPTION); - m_decipherFilter = get_cipher(BLOCK_CIPHER_STR, DECRYPTION); - m_cmac = new MAC_Filter(MAC_STR); - m_encipherPipe.append(m_encipherFilter); - m_decipherPipe.append(m_decipherFilter); - m_macPipe.append(m_cmac); + try + { + m_encipherFilter = get_cipher(BLOCK_CIPHER_STR, ENCRYPTION); + m_decipherFilter = get_cipher(BLOCK_CIPHER_STR, DECRYPTION); + m_cmac = new MAC_Filter(MAC_STR); + m_encipherPipe.append(m_encipherFilter); + m_decipherPipe.append(m_decipherFilter); + m_macPipe.append(m_cmac); + } + catch(Botan::Exception e) + { + m_botanErrorMsg = e.what(); + } } -void -Codec::GenerateWriteKey(const char* userPassword, int passwordLength) +void Codec::GenerateWriteKey(const char *userPassword, int passwordLength) { - PBKDF* pbkdf = get_pbkdf(PBKDF_STR); - pbkdf->set_iterations(PBKDF_ITERATIONS); - pbkdf->change_salt((const byte*)SALT_STR.c_str(), SALT_SIZE); - - SymmetricKey masterKey = - pbkdf->derive_key(KEY_SIZE + IV_DERIVATION_KEY_SIZE, std::string(userPassword, passwordLength)); - - m_writeKey = SymmetricKey(masterKey.bits_of(), KEY_SIZE); - m_ivWriteKey = SymmetricKey(masterKey.bits_of() + KEY_SIZE, IV_DERIVATION_KEY_SIZE); - - m_hasWriteKey = true; + try + { +#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,4) + PBKDF *pbkdf = get_pbkdf(PBKDF_STR); + SymmetricKey masterKey = + pbkdf->derive_key(KEY_SIZE + IV_DERIVATION_KEY_SIZE, std::string(userPassword, passwordLength), + (const byte*)SALT_STR.c_str(), SALT_SIZE, PBKDF_ITERATIONS); +#elif BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,8,0) + S2K* s2k = get_s2k(PBKDF_STR); + s2k->set_iterations(PBKDF_ITERATIONS); + s2k->change_salt((const byte*)SALT_STR.c_str(), SALT_SIZE); + + SymmetricKey masterKey = + s2k->derive_key(KEY_SIZE + IV_DERIVATION_KEY_SIZE, std::string(userPassword, passwordLength)); +#else +#error "This code requires botan 1.8 or newer" +#endif + m_writeKey = SymmetricKey(masterKey.bits_of(), KEY_SIZE); + m_ivWriteKey = SymmetricKey(masterKey.bits_of() + KEY_SIZE, IV_DERIVATION_KEY_SIZE); + + m_hasWriteKey = true; + } + catch(Botan::Exception e) + { + m_botanErrorMsg = e.what(); + } } -void -Codec::DropWriteKey() +void Codec::DropWriteKey() { m_hasWriteKey = false; } -void -Codec::SetReadIsWrite() +void Codec::SetReadIsWrite() { m_readKey = m_writeKey; m_ivReadKey = m_ivWriteKey; m_hasReadKey = m_hasWriteKey; } -void -Codec::SetWriteIsRead() +void Codec::SetWriteIsRead() { m_writeKey = m_readKey; m_ivWriteKey = m_ivReadKey; m_hasWriteKey = m_hasReadKey; } -unsigned char * -Codec::Encrypt(int page, unsigned char* data, bool useWriteKey) +unsigned char* Codec::Encrypt(int page, unsigned char *data, bool useWriteKey) { memcpy(m_page, data, m_pageSize); - m_encipherFilter->set_key(useWriteKey ? m_writeKey : m_readKey); - m_encipherFilter->set_iv(GetIVForPage(page, useWriteKey)); - m_encipherPipe.process_msg(m_page, m_pageSize); - m_encipherPipe.read(m_page, m_encipherPipe.remaining(Pipe::LAST_MESSAGE), Pipe::LAST_MESSAGE); + try + { + m_encipherFilter->set_key(useWriteKey ? m_writeKey : m_readKey); + m_encipherFilter->set_iv(GetIVForPage(page, useWriteKey)); + m_encipherPipe.process_msg(m_page, m_pageSize); + m_encipherPipe.read(m_page, m_encipherPipe.remaining(Pipe::LAST_MESSAGE), Pipe::LAST_MESSAGE); + } + catch(Botan::Exception e) + { + m_botanErrorMsg = e.what(); + } return m_page; //return location of newly ciphered data } -void -Codec::Decrypt(int page, unsigned char* data) +void Codec::Decrypt(int page, unsigned char *data) { - m_decipherFilter->set_key(m_readKey); - m_decipherFilter->set_iv(GetIVForPage(page, false)); - m_decipherPipe.process_msg(data, m_pageSize); - m_decipherPipe.read(data, m_decipherPipe.remaining(Pipe::LAST_MESSAGE), Pipe::LAST_MESSAGE); + try + { + m_decipherFilter->set_key(m_readKey); + m_decipherFilter->set_iv(GetIVForPage(page, false)); + m_decipherPipe.process_msg(data, m_pageSize); + m_decipherPipe.read(data, m_decipherPipe.remaining(Pipe::LAST_MESSAGE), Pipe::LAST_MESSAGE); + } + catch(Botan::Exception e) + { + m_botanErrorMsg = e.what(); + } } -InitializationVector -Codec::GetIVForPage(u32bit page, bool useWriteKey) +InitializationVector Codec::GetIVForPage(u32bit page, bool useWriteKey) { - static unsigned char* intiv[4]; - store_le(page, (byte*)intiv); - m_cmac->set_key(useWriteKey ? m_ivWriteKey : m_ivReadKey); - m_macPipe.process_msg((byte*)intiv, 4); - return m_macPipe.read_all(Pipe::LAST_MESSAGE); + try + { + static unsigned char *intiv[4]; + store_le(page, (byte*)intiv); + m_cmac->set_key(useWriteKey ? m_ivWriteKey : m_ivReadKey); + m_macPipe.process_msg((byte*)intiv, 4); + return m_macPipe.read_all(Pipe::LAST_MESSAGE); + } + catch(Botan::Exception e) + { + m_botanErrorMsg = e.what(); + } +} + +const char* Codec::GetAndResetError() +{ + const char *message = m_botanErrorMsg; + m_botanErrorMsg = 0; + return message; } +#include "codec_c_interface.h" + +void InitializeBotan() { + LibraryInitializer::initialize(); +} +void* InitializeNewCodec(void *db) { + return new Codec(db); +} +void* InitializeFromOtherCodec(const void *otherCodec, void *db) { + return new Codec((Codec*)otherCodec, db); +} +void GenerateWriteKey(void *codec, const char *userPassword, int passwordLength) { + ((Codec*)codec)->GenerateWriteKey(userPassword, passwordLength); +} +void DropWriteKey(void *codec) { + ((Codec*)codec)->DropWriteKey(); +} +void SetWriteIsRead(void *codec) { + ((Codec*)codec)->SetWriteIsRead(); +} +void SetReadIsWrite(void *codec) { + ((Codec*)codec)->SetReadIsWrite(); +} +unsigned char* Encrypt(void *codec, int page, unsigned char *data, Bool useWriteKey) { + ((Codec*)codec)->Encrypt(page, data, useWriteKey); +} +void Decrypt(void *codec, int page, unsigned char *data) { + ((Codec*)codec)->Decrypt(page, data); +} +void SetPageSize(void *codec, int pageSize) { + ((Codec*)codec)->SetPageSize(pageSize); +} +Bool HasReadKey(void *codec) { + return ((Codec*)codec)->HasReadKey(); +} +Bool HasWriteKey(void *codec) { + return ((Codec*)codec)->HasWriteKey(); +} +void* GetDB(void *codec) { + return ((Codec*)codec)->GetDB(); +} +const char* GetAndResetError(void *codec) +{ + return ((Codec*)codec)->GetAndResetError(); +} +void DeleteCodec(void *codec) { + Codec *deleteThisCodec = (Codec*)codec; + delete deleteThisCodec; +} diff --git a/src/wrap/sqlite/codec.h b/src/wrap/sqlite/codec.h index c254f9fde..2f94bcc59 100644 --- a/src/wrap/sqlite/codec.h +++ b/src/wrap/sqlite/codec.h @@ -12,32 +12,6 @@ #include <botan/botan.h> #include <botan/loadstor.h> -#ifdef __cplusplus -extern "C" { -#endif - -#if defined(__BORLANDC__) -#define __STDC__ 1 -#endif - -#include "./sqliteInt.h" - -#if defined(__BORLANDC__) -#undef __STDC__ -#endif - -/* ATTENTION: Macro similar to that in pager.c - * Needed because pager is forward declared when needed most - * TODO: Check in case of new version of SQLite - * ... but it's VERY unlikely to change (it'd break all past DBs) - */ -#include "./os.h" -#define CODEC_PAGER_MJ_PGNO(x) ((PENDING_BYTE/(x))+1) - -#ifdef __cplusplus -} /* End of the 'extern "C"' block */ -#endif - using namespace std; using namespace Botan; @@ -57,6 +31,9 @@ const string PBKDF_STR = "PBKDF2(SHA-160)"; //SALT_STR: Hard coded salt used to derive the key from the passphrase. const string SALT_STR = "&g#nB'9]"; +//SALT_SIZE: Size of the salt in bytes (as given in SALT_STR) +const int SALT_SIZE = 64/8; //64 bit, 8 byte salt + //MAC_STR: CMAC used to derive the IV that is used for db page //encryption const string MAC_STR = "CMAC(Twofish)"; @@ -65,9 +42,6 @@ const string MAC_STR = "CMAC(Twofish)"; //process. const int PBKDF_ITERATIONS = 10000; -//SALT_SIZE: Size of the salt in bytes (as given in SALT_STR) -const int SALT_SIZE = 64/8; //64 bit, 8 byte salt - //KEY_SIZE: Size of the encryption key. Note that XTS splits the key //between two ciphers, so if you're using XTS, double the intended key //size. (ie, "AES-128/XTS" should have a 256 bit KEY_SIZE) @@ -77,29 +51,33 @@ const int KEY_SIZE = 512/8; //512 bit, 64 byte key. (256 bit XTS key) //above. const int IV_DERIVATION_KEY_SIZE = 256/8; //256 bit, 32 byte key +//This is definited in sqlite.h and very unlikely to change +#define SQLITE_MAX_PAGE_SIZE 32768 + class Codec { public: Codec(void *db); - Codec(const Codec& other, void *db); + Codec(const Codec* other, void *db); - void GenerateWriteKey(const char* userPassword, int passwordLength); + void GenerateWriteKey(const char *userPassword, int passwordLength); void DropWriteKey(); void SetWriteIsRead(); void SetReadIsWrite(); - unsigned char* Encrypt(int page, unsigned char* data, bool useWriteKey); - void Decrypt(int page, unsigned char* data); + unsigned char* Encrypt(int page, unsigned char *data, bool useWriteKey); + void Decrypt(int page, unsigned char *data); void SetPageSize(int pageSize) { m_pageSize = pageSize; } bool HasReadKey() { return m_hasReadKey; } bool HasWriteKey() { return m_hasWriteKey; } void* GetDB() { return m_db; } + const char* GetAndResetError(); private: - bool m_hasReadKey; - bool m_hasWriteKey; + bool m_hasReadKey; + bool m_hasWriteKey; SymmetricKey m_readKey, @@ -118,7 +96,8 @@ private: int m_pageSize; unsigned char m_page[SQLITE_MAX_PAGE_SIZE]; - void* m_db; + void *m_db; + const char *m_botanErrorMsg; InitializationVector GetIVForPage(u32bit page, bool useWriteKey); void InitializeCodec(void *db); diff --git a/src/wrap/sqlite/codecext.c b/src/wrap/sqlite/codecext.c new file mode 100644 index 000000000..66f13b9b6 --- /dev/null +++ b/src/wrap/sqlite/codecext.c @@ -0,0 +1,242 @@ +/* + * Encryption codec implementation + * (C) 2010 Olivier de Gaalon + * + * Distributed under the terms of the Botan license + */ + +#ifndef SQLITE_OMIT_DISKIO +#ifdef SQLITE_HAS_CODEC + +#include "codec_c_interface.h" + +Bool HandleError(void *pCodec) +{ + const char *error = GetAndResetError(pCodec); + if (error) { + sqlite3Error((sqlite3*)GetDB(pCodec), SQLITE_ERROR, "Botan Error: %s", error); + return 1; + } + return 0; +} + +// Guessing that "see" is related to SQLite Encryption Extension" (the semi-official, for-pay, encryption codec) +// Just as useful for initializing Botan. +void sqlite3_activate_see(const char *info) +{ + InitializeBotan(); +} + +// Free the encryption codec, called from pager.c (address passed in sqlite3PagerSetCodec) +void sqlite3PagerFreeCodec(void *pCodec) +{ + if (pCodec) + DeleteCodec(pCodec); +} + +// Report the page size to the codec, called from pager.c (address passed in sqlite3PagerSetCodec) +void sqlite3CodecSizeChange(void *pCodec, int pageSize, int nReserve) +{ + SetPageSize(pCodec, pageSize); +} + +// Encrypt/Decrypt functionality, called by pager.c +void* sqlite3Codec(void *pCodec, void *data, Pgno nPageNum, int nMode) +{ + if (pCodec == NULL) //Db not encrypted + return data; + + switch(nMode) + { + case 0: // Undo a "case 7" journal file encryption + case 2: // Reload a page + case 3: // Load a page + if (HasReadKey(pCodec)) + Decrypt(pCodec, nPageNum, (unsigned char*) data); + break; + case 6: // Encrypt a page for the main database file + if (HasWriteKey(pCodec)) + data = Encrypt(pCodec, nPageNum, (unsigned char*) data, 1); + break; + case 7: // Encrypt a page for the journal file + /* + *Under normal circumstances, the readkey is the same as the writekey. However, + *when the database is being rekeyed, the readkey is not the same as the writekey. + *(The writekey is the "destination key" for the rekey operation and the readkey + *is the key the db is currently encrypted with) + *Therefore, for case 7, when the rollback is being written, always encrypt using + *the database's readkey, which is guaranteed to be the same key that was used to + *read and write the original data. + */ + if (HasReadKey(pCodec)) + data = Encrypt(pCodec, nPageNum, (unsigned char*) data, 0); + break; + } + + HandleError(pCodec); + + return data; +} + +int sqlite3CodecAttach(sqlite3 *db, int nDb, const void *zKey, int nKey) +{ + void *pCodec; + + if (zKey == NULL || nKey <= 0) + { + // No key specified, could mean either use the main db's encryption or no encryption + if (nDb != 0 && nKey < 0) + { + //Is an attached database, therefore use the key of main database, if main database is encrypted + void *pMainCodec = sqlite3PagerGetCodec(sqlite3BtreePager(db->aDb[0].pBt)); + if (pMainCodec != NULL) + { + pCodec = InitializeFromOtherCodec(pMainCodec, db); + sqlite3PagerSetCodec(sqlite3BtreePager(db->aDb[nDb].pBt), + sqlite3Codec, + sqlite3CodecSizeChange, + sqlite3PagerFreeCodec, pCodec); + } + } + } + else + { + // Key specified, setup encryption key for database + pCodec = InitializeNewCodec(db); + GenerateWriteKey(pCodec, (const char*) zKey, nKey); + SetReadIsWrite(pCodec); + sqlite3PagerSetCodec(sqlite3BtreePager(db->aDb[nDb].pBt), + sqlite3Codec, + sqlite3CodecSizeChange, + sqlite3PagerFreeCodec, pCodec); + } + + if (HandleError(pCodec)) + return SQLITE_ERROR; + + return SQLITE_OK; +} + +void sqlite3CodecGetKey(sqlite3* db, int nDb, void **zKey, int *nKey) +{ + // The unencrypted password is not stored for security reasons + // therefore always return NULL + *zKey = NULL; + *nKey = -1; +} + +int sqlite3_key(sqlite3 *db, const void *zKey, int nKey) +{ + // The key is only set for the main database, not the temp database + return sqlite3CodecAttach(db, 0, zKey, nKey); +} + +int sqlite3_rekey(sqlite3 *db, const void *zKey, int nKey) +{ + // Changes the encryption key for an existing database. + int rc = SQLITE_ERROR; + Btree *pbt = db->aDb[0].pBt; + Pager *pPager = sqlite3BtreePager(pbt); + void *pCodec = sqlite3PagerGetCodec(pPager); + + if ((zKey == NULL || nKey == 0) && pCodec == NULL) + { + // Database not encrypted and key not specified. Do nothing + return SQLITE_OK; + } + + if (pCodec == NULL) + { + // Database not encrypted, but key specified. Encrypt database + pCodec = InitializeNewCodec(db); + GenerateWriteKey(pCodec, (const char*) zKey, nKey); + + if (HandleError(pCodec)) + return SQLITE_ERROR; + + sqlite3PagerSetCodec(pPager, sqlite3Codec, sqlite3CodecSizeChange, sqlite3PagerFreeCodec, pCodec); + } + else if (zKey == NULL || nKey == 0) + { + // Database encrypted, but key not specified. Decrypt database + // Keep read key, drop write key + DropWriteKey(pCodec); + } + else + { + // Database encrypted and key specified. Re-encrypt database with new key + // Keep read key, change write key to new key + GenerateWriteKey(pCodec, (const char*) zKey, nKey); + if (HandleError(pCodec)) + return SQLITE_ERROR; + } + + // Start transaction + rc = sqlite3BtreeBeginTrans(pbt, 1); + if (rc == SQLITE_OK) + { + // Rewrite all pages using the new encryption key (if specified) + int nPageCount = -1; + int rc = sqlite3PagerPagecount(pPager, &nPageCount); + Pgno nPage = (Pgno) nPageCount; + + Pgno nSkip = PAGER_MJ_PGNO(pPager); + DbPage *pPage; + + Pgno n; + for (n = 1; rc == SQLITE_OK && n <= nPage; n++) + { + if (n == nSkip) + continue; + + rc = sqlite3PagerGet(pPager, n, &pPage); + + if (!rc) + { + rc = sqlite3PagerWrite(pPage); + sqlite3PagerUnref(pPage); + } + else + sqlite3Error(db, SQLITE_ERROR, "%s", "Error while rekeying database page. Transaction Canceled."); + } + } + else + sqlite3Error(db, SQLITE_ERROR, "%s", "Error beginning rekey transaction. Make sure that the current encryption key is correct."); + + if (rc == SQLITE_OK) + { + // All good, commit + rc = sqlite3BtreeCommit(pbt); + + if (rc == SQLITE_OK) + { + //Database rekeyed and committed successfully, update read key + if (HasWriteKey(pCodec)) + SetReadIsWrite(pCodec); + else //No write key == no longer encrypted + sqlite3PagerSetCodec(pPager, NULL, NULL, NULL, NULL); + } + else + { + //FIXME: can't trigger this, not sure if rollback is needed, reference implementation didn't rollback + sqlite3Error(db, SQLITE_ERROR, "%s", "Could not commit rekey transaction."); + } + } + else + { + // Rollback, rekey failed + sqlite3BtreeRollback(pbt); + + // go back to read key + if (HasReadKey(pCodec)) + SetWriteIsRead(pCodec); + else //Database wasn't encrypted to start with + sqlite3PagerSetCodec(pPager, NULL, NULL, NULL, NULL); + } + + return rc; +} + +#endif // SQLITE_HAS_CODEC + +#endif // SQLITE_OMIT_DISKIO diff --git a/src/wrap/sqlite/codecext.cpp b/src/wrap/sqlite/codecext.cpp deleted file mode 100644 index e542df975..000000000 --- a/src/wrap/sqlite/codecext.cpp +++ /dev/null @@ -1,261 +0,0 @@ -/* - * SQLite3 encryption extention codec - * (C) 2010 Olivier de Gaalon - * - * Distributed under the terms of the Botan license - */ - -#ifndef SQLITE_OMIT_DISKIO -#ifdef SQLITE_HAS_CODEC - -#include "codec.h" -#include "sqlite3.h" - -// Required to implement, called from pragma.c, guessing that "see" is related to the -// "SQLite Encryption Extension" (the semi-official, for-pay, encryption codec) -extern "C" -void sqlite3_activate_see(const char *info) { } - -// Free the encryption codec, called from pager.c (address passed in sqlite3PagerSetCodec) -extern "C" -void sqlite3PagerFreeCodec(void *pCodec) -{ - if (pCodec) - delete (Codec*) pCodec; -} - -// Report the page size to the codec, called from pager.c (address passed in sqlite3PagerSetCodec) -extern "C" -void sqlite3CodecSizeChange(void *pCodec, int pageSize, int nReserve) -{ - Codec* codec = (Codec*) pCodec; - codec->SetPageSize(pageSize); -} - -// Encrypt/Decrypt functionality, called by pager.c -extern "C" -void* sqlite3Codec(void* pCodec, void* data, Pgno nPageNum, int nMode) -{ - if (pCodec == NULL) //Db not encrypted - return data; - - Codec* codec = (Codec*) pCodec; - - try - { - switch(nMode) - { - case 0: // Undo a "case 7" journal file encryption - case 2: // Reload a page - case 3: // Load a page - if (codec->HasReadKey()) - codec->Decrypt(nPageNum, (unsigned char*) data); - break; - case 6: // Encrypt a page for the main database file - if (codec->HasWriteKey()) - data = codec->Encrypt(nPageNum, (unsigned char*) data, true); - break; - case 7: // Encrypt a page for the journal file - /* - *Under normal circumstances, the readkey is the same as the writekey. However, - *when the database is being rekeyed, the readkey is not the same as the writekey. - *(The writekey is the "destination key" for the rekey operation and the readkey - *is the key the db is currently encrypted with) - *Therefore, for case 7, when the rollback is being written, always encrypt using - *the database's readkey, which is guaranteed to be the same key that was used to - *read and write the original data. - */ - if (codec->HasReadKey()) - data = codec->Encrypt(nPageNum, (unsigned char*) data, false); - break; - } - } - catch(Botan::Exception e) - { - sqlite3Error((sqlite3*)codec->GetDB(), SQLITE_ERROR, "Botan Error: %s", e.what()); - } - - return data; -} - -//These functions are defined in pager.c -extern "C" void* sqlite3PagerGetCodec(Pager *pPager); -extern "C" void sqlite3PagerSetCodec( - Pager *pPager, - void *(*xCodec)(void*,void*,Pgno,int), - void (*xCodecSizeChng)(void*,int,int), - void (*xCodecFree)(void*), - void *pCodec -); - - -extern "C" -int sqlite3CodecAttach(sqlite3* db, int nDb, const void* zKey, int nKey) -{ - try { - if (zKey == NULL || nKey <= 0) - { - // No key specified, could mean either use the main db's encryption or no encryption - if (nDb != 0 && nKey < 0) - { - //Is an attached database, therefore use the key of main database, if main database is encrypted - Codec* mainCodec = (Codec*) sqlite3PagerGetCodec(sqlite3BtreePager(db->aDb[0].pBt)); - if (mainCodec != NULL) - { - Codec* codec = new Codec(*mainCodec, db); - sqlite3PagerSetCodec(sqlite3BtreePager(db->aDb[nDb].pBt), - sqlite3Codec, - sqlite3CodecSizeChange, - sqlite3PagerFreeCodec, codec); - } - } - } - else - { - // Key specified, setup encryption key for database - Codec* codec = new Codec(db); - codec->GenerateWriteKey((const char*) zKey, nKey); - codec->SetReadIsWrite(); - sqlite3PagerSetCodec(sqlite3BtreePager(db->aDb[nDb].pBt), - sqlite3Codec, - sqlite3CodecSizeChange, - sqlite3PagerFreeCodec, codec); - } - } - catch(Botan::Exception e) { - sqlite3Error(db, SQLITE_ERROR, "Botan Error: %s", e.what()); - return SQLITE_ERROR; - } - return SQLITE_OK; -} - -extern "C" -void sqlite3CodecGetKey(sqlite3* db, int nDb, void** zKey, int* nKey) -{ - // The unencrypted password is not stored for security reasons - // therefore always return NULL - *zKey = NULL; - *nKey = -1; -} - -extern "C" -int sqlite3_key(sqlite3 *db, const void *zKey, int nKey) -{ - // The key is only set for the main database, not the temp database - return sqlite3CodecAttach(db, 0, zKey, nKey); -} - -extern "C" -int sqlite3_rekey(sqlite3 *db, const void *zKey, int nKey) -{ - // Changes the encryption key for an existing database. - int rc = SQLITE_ERROR; - Btree* pbt = db->aDb[0].pBt; - Pager* pPager = sqlite3BtreePager(pbt); - Codec* codec = (Codec*) sqlite3PagerGetCodec(pPager); - - if ((zKey == NULL || nKey == 0) && codec == NULL) - { - // Database not encrypted and key not specified. Do nothing - return SQLITE_OK; - } - - if (codec == NULL) - { - // Database not encrypted, but key specified. Encrypt database - try { - codec = new Codec(db); - codec->GenerateWriteKey((const char*) zKey, nKey); - } catch (Botan::Exception e) { - sqlite3Error(db, SQLITE_ERROR, "Botan Error %s", e.what()); - return SQLITE_ERROR; - } - sqlite3PagerSetCodec(pPager, sqlite3Codec, sqlite3CodecSizeChange, sqlite3PagerFreeCodec, codec); - } - else if (zKey == NULL || nKey == 0) - { - // Database encrypted, but key not specified. Decrypt database - // Keep read key, drop write key - codec->DropWriteKey(); - } - else - { - // Database encrypted and key specified. Re-encrypt database with new key - // Keep read key, change write key to new key - try { - codec->GenerateWriteKey((const char*) zKey, nKey); - } catch (Botan::Exception e) { - sqlite3Error(db, SQLITE_ERROR, "Botan Error %s", e.what()); - return SQLITE_ERROR; - } - } - - // Start transaction - rc = sqlite3BtreeBeginTrans(pbt, 1); - if (rc == SQLITE_OK) - { - // Rewrite all pages using the new encryption key (if specified) - int nPageCount = -1; - int rc = sqlite3PagerPagecount(pPager, &nPageCount); - Pgno nPage = (Pgno) nPageCount; - int pageSize = sqlite3BtreeGetPageSize(pbt); - //Can't use SQLite3 macro here since pager is forward declared...sigh - Pgno nSkip = CODEC_PAGER_MJ_PGNO(pageSize); - DbPage *pPage; - - for (Pgno n = 1; rc == SQLITE_OK && n <= nPage; n++) - { - if (n == nSkip) - continue; - - rc = sqlite3PagerGet(pPager, n, &pPage); - - if (!rc) - { - rc = sqlite3PagerWrite(pPage); - sqlite3PagerUnref(pPage); - } - else - sqlite3Error(db, SQLITE_ERROR, "%s", "Error while rekeying database page. Transaction Canceled."); - } - } - else - sqlite3Error(db, SQLITE_ERROR, "%s", "Error beginning rekey transaction. Make sure that the current encryption key is correct."); - - if (rc == SQLITE_OK) - { - // All good, commit - rc = sqlite3BtreeCommit(pbt); - - if (rc == SQLITE_OK) - { - //Database rekeyed and committed successfully, update read key - if (codec->HasWriteKey()) - codec->SetReadIsWrite(); - else //No write key == no longer encrypted - sqlite3PagerSetCodec(pPager, NULL, NULL, NULL, NULL); - } - else - { - //FIXME: can't trigger this, not sure if rollback is needed, reference implementation didn't rollback - sqlite3Error(db, SQLITE_ERROR, "%s", "Could not commit rekey transaction."); - } - } - else - { - // Rollback, rekey failed - sqlite3BtreeRollback(pbt); - - // go back to read key - if (codec->HasReadKey()) - codec->SetWriteIsRead(); - else //Database wasn't encrypted to start with - sqlite3PagerSetCodec(pPager, NULL, NULL, NULL, NULL); - } - - return rc; -} - -#endif // SQLITE_HAS_CODEC - -#endif // SQLITE_OMIT_DISKIO diff --git a/src/wrap/sqlite/readme.txt b/src/wrap/sqlite/readme.txt index e131f9e91..b692aec9b 100644 --- a/src/wrap/sqlite/readme.txt +++ b/src/wrap/sqlite/readme.txt @@ -1,36 +1,31 @@ Build instructions for Botan SQLite3 codec --- -1. Requires Botan 1.8.8 or later (earlier versions OK if you switch to - CBC mode from XTS) +1. Requires Botan 1.9.0 or later -2. Download SQLite3 version 3.6.17 or later, get the version "as - extracted from the source control system", NOT the amalgamation. +2. Download and extract SQLite3 version 3.7.0.1 or later (previous + versions may work, untested) -3. Apply the patch "sqlite.diff" [*]: +3. From the extracted sqlite folder, apply the patch "sqlite3.diff": $ patch -p0 < ../sqlite.diff patching file Makefile.in - patching file src/pager.c + patching file sqlite3.c - If the patch to pager.c fails for some reason (ie, changes in - SQLite3), all that need be done is remove the "static" keyword from - the functions sqlite3PagerSetCodec and sqlite3PagerGetCodec. + If the patch to fails for some reason (ie, changes in SQLite3), it + should be trivial to do it manually. -5. Create a folder called "botan" in the SQLite3 src dir and copy - "codec.cpp", "codec.h", and "codecext.cpp" into it. +4. Copy all files inside the "src" directory into the Sqlite3 directory + (codec.cpp, codec.h, codec_c_interface.h, codecext.c) -6. As desired, edit the constants in codec.h to tweak the encryption +5. As desired, edit the constants in codec.h to tweak the encryption type to your needs. (Currently, Twofish/XTS with 256 bit key) -7. Run ./configure in the SQLite3 root directory with the - "--disable-amalgamation" and (if desired) "--disable-shared" - arguments, and then run make. +6. "./configure" and "make" Sqlite3 And to make sure it all worked... -8. Make the test_sqlite.cpp file: +7. Make the test_sqlite.cpp file: $ g++ test_sqlite.cpp -o test_sqlite -lbotan /path/to/libsqlite3.a -9. Run it +8. Run it $ ./test_sqlite -10. Look for "All seems good" - +9. Look for "All seems good"
\ No newline at end of file diff --git a/src/wrap/sqlite/sqlite.diff b/src/wrap/sqlite/sqlite.diff deleted file mode 100644 index 1a3ef764c..000000000 --- a/src/wrap/sqlite/sqlite.diff +++ /dev/null @@ -1,77 +0,0 @@ ---- Makefile.in.orig 2009-10-30 09:34:59.000000000 -0400 -+++ Makefile.in 2010-01-21 22:51:22.000000000 -0500 -@@ -133,7 +133,10 @@ - GCOV_LDFLAGS1 = -lgcov - USE_GCOV = @USE_GCOV@ - LTCOMPILE_EXTRAS += $(GCOV_CFLAGS$(USE_GCOV)) -+LTCOMPILE_EXTRAS += -DSQLITE_HAS_CODEC - LTLINK_EXTRAS += $(GCOV_LDFLAGS$(USE_GCOV)) -+LTLINK_EXTRAS += -lstdc++ -+LTLINK_EXTRAS += -lbotan - - - # The directory into which to store package information for -@@ -176,7 +179,8 @@ - table.lo tokenize.lo trigger.lo update.lo \ - util.lo vacuum.lo \ - vdbe.lo vdbeapi.lo vdbeaux.lo vdbeblob.lo vdbemem.lo \ -- walker.lo where.lo utf.lo vtab.lo -+ walker.lo where.lo utf.lo vtab.lo \ -+ codec.lo codecext.lo - - # Object files for the amalgamation. - # -@@ -275,7 +279,10 @@ - $(TOP)/src/vdbeInt.h \ - $(TOP)/src/vtab.c \ - $(TOP)/src/walker.c \ -- $(TOP)/src/where.c -+ $(TOP)/src/where.c \ -+ $(TOP)/src/botan/codec.cpp \ -+ $(TOP)/src/botan/codecext.cpp \ -+ $(TOP)/src/botan/codec.h - - # Generated source code files - # -@@ -411,6 +418,7 @@ - $(TOP)/src/sqlite3ext.h \ - $(TOP)/src/sqliteInt.h \ - $(TOP)/src/vdbe.h \ -+ $(TOP)/src/botan/codec.h \ - $(TOP)/src/vdbeInt.h \ - parse.h \ - config.h -@@ -622,6 +630,13 @@ - notify.lo: $(TOP)/src/notify.c $(HDR) - $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/notify.c - -+codec.lo: $(TOP)/src/botan/codec.cpp $(HDR) $(TOP)/src/botan/codec.h -+ $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/botan/codec.cpp -+ -+codecext.lo: $(TOP)/src/botan/codecext.cpp $(HDR) $(TOP)/src/botan/codec.h -+ $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/botan/codecext.cpp -+ -+ - pager.lo: $(TOP)/src/pager.c $(HDR) $(TOP)/src/pager.h - $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/pager.c - ---- src/pager.c.orig 2009-10-30 17:01:07.000000000 -0400 -+++ src/pager.c 2010-01-21 22:51:53.000000000 -0500 -@@ -5046,7 +5046,7 @@ - /* - ** Set or retrieve the codec for this pager - */ --static void sqlite3PagerSetCodec( -+void sqlite3PagerSetCodec( - Pager *pPager, - void *(*xCodec)(void*,void*,Pgno,int), - void (*xCodecSizeChng)(void*,int,int), -@@ -5060,7 +5060,7 @@ - pPager->pCodec = pCodec; - pagerReportSize(pPager); - } --static void *sqlite3PagerGetCodec(Pager *pPager){ -+void *sqlite3PagerGetCodec(Pager *pPager){ - return pPager->pCodec; - } - #endif diff --git a/src/wrap/sqlite/sqlite3.diff b/src/wrap/sqlite/sqlite3.diff new file mode 100644 index 000000000..7ea89f002 --- /dev/null +++ b/src/wrap/sqlite/sqlite3.diff @@ -0,0 +1,46 @@ +--- Makefile.in.orig 2010-08-05 00:15:13.000000000 +0800 ++++ Makefile.in 2010-08-16 15:09:58.000000000 +0800 +@@ -65,7 +65,7 @@ + libLTLIBRARIES_INSTALL = $(INSTALL) + LTLIBRARIES = $(lib_LTLIBRARIES) + libsqlite3_la_LIBADD = +-am_libsqlite3_la_OBJECTS = sqlite3.lo ++am_libsqlite3_la_OBJECTS = sqlite3.lo codec.lo + libsqlite3_la_OBJECTS = $(am_libsqlite3_la_OBJECTS) + binPROGRAMS_INSTALL = $(INSTALL_PROGRAM) + PROGRAMS = $(bin_PROGRAMS) +@@ -209,10 +209,10 @@ + sharedstatedir = @sharedstatedir@ + sysconfdir = @sysconfdir@ + target_alias = @target_alias@ +-AM_CFLAGS = @THREADSAFE_FLAGS@ @DYNAMIC_EXTENSION_FLAGS@ -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_RTREE ++AM_CFLAGS = @THREADSAFE_FLAGS@ @DYNAMIC_EXTENSION_FLAGS@ -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_RTREE -DSQLITE_HAS_CODEC + lib_LTLIBRARIES = libsqlite3.la + libsqlite3_la_SOURCES = sqlite3.c +-libsqlite3_la_LDFLAGS = -no-undefined -version-info 8:6:8 ++libsqlite3_la_LDFLAGS = -no-undefined -version-info 8:6:8 -lstdc++ -lbotan + sqlite3_SOURCES = shell.c sqlite3.h + sqlite3_LDADD = $(top_builddir)/libsqlite3.la @READLINE_LIBS@ + sqlite3_DEPENDENCIES = $(top_builddir)/libsqlite3.la +@@ -349,6 +349,13 @@ + @am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi + @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ + @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< ++ ++codec.lo: ++@am__fastdepCC_TRUE@ if $(LTCOMPILE) codec.cpp -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ ++@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + @am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + + mostlyclean-libtool: +--- sqlite3.c.orig 2010-08-05 00:14:22.000000000 +0800 ++++ sqlite3.c 2010-08-16 15:35:55.000000000 +0800 +@@ -118684,3 +118684,5 @@ + #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */ + + /************** End of fts3_icu.c ********************************************/ ++ ++#include "codecext.c" diff --git a/src/wrap/sqlite/test_sqlite.cpp b/src/wrap/sqlite/test_sqlite.cpp index 8d8cad946..6a22cd612 100644 --- a/src/wrap/sqlite/test_sqlite.cpp +++ b/src/wrap/sqlite/test_sqlite.cpp @@ -5,6 +5,8 @@ * Distributed under the terms of the Botan license */ +#define SQLITE_HAS_CODEC 1 + #include <sqlite3.h> #include <stdio.h> |