Sqlite 3.7.14.1 xxtea 加密演算法

weixin_34162629發表於2016-04-07

http://blog.csdn.net/wzq9706/article/details/8133314

最近在研空Sqlite加密演算法,東拼西湊,還沒研究出AES怎麼用,歡迎指正,交流

1.從官方下載最新版本的Sqlite目前

www.sqlite.org

是:如sqlite-amalgamation-3071401.zip

 

2.在VS中新增一個空專案

3.解壓sqlite-amalgamation-3071401.zip

複製出裡面的sqlite3.h和sqlite3.c檔案,放在工程目錄中,並新增到工程

4.在工程中新增"sqlite3crypt.h",並新增以下程式碼

 

#ifndef  DCG_SQLITE_CRYPT_FUNC_
#define  DCG_SQLITE_CRYPT_FUNC_

//#ifdef SQLITE_HAS_CODEC

typedef unsigned char BYTE;
// 加密結構
#define CRYPT_OFFSET 8
typedef struct _CryptBlock {

    BYTE* ReadKey; // 讀資料庫和寫入事務的金鑰
    BYTE* WriteKey; // 寫入資料庫的金鑰
    int PageSize; // 頁的大小
    BYTE* Data;

} CryptBlock, *LPCryptBlock;

#ifndef DB_KEY_LENGTH_BYTE        // 金鑰長度
#define DB_KEY_LENGTH_BYTE   16   // 金鑰長度
#endif

#ifndef DB_KEY_PADDING            // 金鑰位數不足時補充的字元
#define DB_KEY_PADDING       0x33 // 金鑰位數不足時補充的字元
#endif

// 加密函式
int sqlite3_encrypt(unsigned char * pData, unsigned int data_len, unsigned char * key, unsigned int len_of_key);
 
// 解密函式
int sqlite3_dencrypt(unsigned char * pData, unsigned int data_len, unsigned char * key, unsigned int len_of_key);

// ===
// 被sqlite3呼叫的加/解密函式
void* sqlite3Codec(void *pArg, void *data, int nPageNum, int nMode);

// 釋放而加/密記憶體的回撥
void sqlite3CodecFree(void* arg);

// 驗證密碼介面
unsigned char* DeriveKey(const void *pKey, int nKeyLen);

// 建立或更新一個頁的加密演算法索引.此函式會申請緩衝區.
LPCryptBlock CreateCryptBlock(unsigned char* hKey, int nPageSize, LPCryptBlock pExisting);

//#endif // SQLITE_HAS_CODEC
#endif

5.在工程中新增"sqlite3crypt.c",並新增以下程式碼

#include "sqlite3crypt.h"
#include "sqlite3.h"
#include <memory.h>
#include <stdlib.h>

int xxtea( int * v, int n , int * k ) {  
    unsigned int z/*=v[n-1]*/, y=v[0], sum=0,  e,    DELTA=0x9e3779b9 ;  
    int m, p, q ;  
    if ( n>1) {  
        /* Coding Part */  
        z = v[n-1];  
        q = 6+52/n ;  
        while ( q-- > 0 ) {  
            sum += DELTA ;  
            e = sum >> 2&3 ;  
            for ( p = 0 ; p < n-1 ; p++ ){  
                y = v[p+1],  
                    z = v[p] += (z>>5^y<<2)+(y>>3^z<<4)^(sum^y)+(k[p&3^e]^z);  
            }  
            y = v[0] ;  
            z = v[n-1] += (z>>5^y<<2)+(y>>3^z<<4)^(sum^y)+(k[p&3^e]^z);  
        }  
        return 0 ;  

        /* Decoding Part */  
    }else if ( n <-1 ) {  
        n = -n ;  
        q = 6+52/n ;  
        sum = q*DELTA ;  
        while (sum != 0) {  
            e = sum>>2 & 3 ;  
            for (p = n-1 ; p > 0 ; p-- ){  
                z = v[p-1],  
                    y = v[p] -= (z>>5^y<<2)+(y>>3^z<<4)^(sum^y)+(k[p&3^e]^z);  
            }  
            z = v[n-1] ;  
            y = v[0] -= (z>>5^y<<2)+(y>>3^z<<4)^(sum^y)+(k[p&3^e]^z);  
            sum -= DELTA ;   
        }  
        return 0 ;  
    }  

    return 1 ;  
} 

// 其它演算法,未實現
 // ==============================================================================
int sqlite3_encrypt(unsigned char * pData, unsigned int data_len, unsigned char * key, unsigned int len_of_key)
{
    return 0;
}

// 其它演算法,未實現
// ==============================================================================
int sqlite3_dencrypt( unsigned char * pData, unsigned int data_len, unsigned char * key, unsigned int len_of_key)
{
    
    return 0;
}

// ==============================================================================
void * sqlite3Codec(void *pArg, void *data, int nPageNum, int nMode) {

     LPCryptBlock pBlock = (LPCryptBlock) pArg;
       unsigned int dwPageSize = 0;
       int len = 0;
 
       if (!pBlock)
              return data;
       // 確保pager的頁長度和加密塊的頁長度相等.如果改變,就需要調整.
       if (nMode != 2) {
           int a = 0;
           a++;
             // PgHdr *pageHeader;
 
             // pageHeader = DATA_TO_PGHDR(data);
             // if (pageHeader->pPager->pageSize != pBlock->PageSize) {
                    // CreateCryptBlock(0, pageHeader->pPager, pBlock);
              //}
       }
 
       switch (nMode) {
       case 0: // Undo a "case 7" journal file encryption
       case 2: //過載一個頁
           case 3: //載入一個頁  
        if (!pBlock->ReadKey) break;  
  
        len = 0 - (pBlock->PageSize / 4);  
        xxtea(data, len, pBlock->ReadKey);  
  
        break;  
    case 6: 

        //加密一個主資料庫檔案的頁  
        if (!pBlock->WriteKey) break;  
  
        memcpy(pBlock->Data + CRYPT_OFFSET, data, pBlock->PageSize);  
        data = pBlock->Data + CRYPT_OFFSET;  
  
  
        len = pBlock->PageSize / 4;  
        xxtea(data , len, pBlock->WriteKey);  
        break;  

    case 7:
        // 加密事務檔案的頁
        if (!pBlock->ReadKey) break;  
        memcpy(pBlock->Data + CRYPT_OFFSET, data, pBlock->PageSize);  
        data = pBlock->Data + CRYPT_OFFSET;  
        len = pBlock->PageSize / 4;  
        xxtea(data, len, pBlock->ReadKey);  
        break; 
           /*
       case 3: //載入一個頁
              if (!pBlock->ReadKey)
                     break;
 
              memcpy(pBlock->Data + CRYPT_OFFSET, data, pBlock->PageSize);
              data = pBlock->Data + CRYPT_OFFSET;
              dwPageSize = pBlock->PageSize;
                
              // 解密
              sqlite3_dencrypt((BYTE*)data, dwPageSize, pBlock->ReadKey, DB_KEY_LENGTH_BYTE);
 
              break;
 
       case 6: //加密一個主資料庫檔案的頁
              if (!pBlock->WriteKey)
                     break;
 
              memcpy(pBlock->Data + CRYPT_OFFSET, data, pBlock->PageSize);
              data = pBlock->Data + CRYPT_OFFSET;
              dwPageSize = pBlock->PageSize;
 
              // 加密
              sqlite3_encrypt((BYTE*)data, dwPageSize, pBlock->WriteKey, DB_KEY_LENGTH_BYTE); 
 
              break;
 
       case 7: 
              // 加密事務檔案的頁
              // 在正常環境下, 讀金鑰和寫金鑰相同. 當資料庫是被重新加密的,讀金鑰和寫金鑰未必相同.
              // 回滾事務必要用資料庫檔案的原始金鑰寫入.因此,當一次回滾被寫入,總是用資料庫的讀金鑰,
              // 這是為了保證與讀取原始資料的金鑰相同.
              //
              if (!pBlock->ReadKey)
                     break;
 
              memcpy(pBlock->Data + CRYPT_OFFSET, data, pBlock->PageSize);
              data = pBlock->Data + CRYPT_OFFSET;
              dwPageSize = pBlock->PageSize;
 
              sqlite3_encrypt((BYTE*)data, dwPageSize, pBlock->ReadKey, DB_KEY_LENGTH_BYTE);/*呼叫我的加密函式*/
 
             // break;*/
       }

       return data;
}

// ==============================================================================
void sqlite3CodecFree(void* pArg) {
    if (pArg)
    {
        LPCryptBlock pBlock = (LPCryptBlock)pArg;
        //銷燬讀金鑰.
        if (pBlock->ReadKey) {
            sqlite3_free(pBlock->ReadKey);
        }

        //如果寫金鑰存在並且不等於讀金鑰,也銷燬.

        if (pBlock->WriteKey && pBlock->WriteKey != pBlock->ReadKey) {
            sqlite3_free(pBlock->WriteKey);
        }

        if (pBlock->Data) {
            sqlite3_free(pBlock->Data);
        }

        //釋放加密塊.
        sqlite3_free(pBlock);
    }
}


// =====================================================================================
LPCryptBlock CreateCryptBlock(unsigned char* hKey, int nPageSize, LPCryptBlock pExisting)
{
    LPCryptBlock pBlock;

    if (!pExisting) //建立新加密塊
    {
        pBlock = sqlite3_malloc(sizeof(CryptBlock));
        memset(pBlock, 0, sizeof(CryptBlock));
        pBlock->ReadKey = hKey;
        pBlock->WriteKey = hKey;
        pBlock->PageSize = nPageSize;
        pBlock->Data = (unsigned char*) sqlite3_malloc(
            pBlock->PageSize + CRYPT_OFFSET);
    }
    else //更新存在的加密塊
    {
        pBlock = pExisting;

        if (pBlock->PageSize != nPageSize && !pBlock->Data) {
            sqlite3_free(pBlock->Data);
            pBlock->PageSize = nPageSize;

            pBlock->Data = (unsigned char*) sqlite3_malloc(
                pBlock->PageSize + CRYPT_OFFSET);
        }
    }

    memset(pBlock->Data, 0, pBlock->PageSize + CRYPT_OFFSET);

    return pBlock;
}

// 從使用者提供的緩衝區中得到一個加密金鑰
// =====================================================================================
unsigned char * DeriveKey(const void *pKey, int nKeyLen)
{
    unsigned char * hKey = 0;
    int j;

    if (pKey == 0 || nKeyLen == 0)
    {
        return 0;
    }

    hKey = sqlite3_malloc(DB_KEY_LENGTH_BYTE + 1);
    if (hKey == 0)
    {
        return 0;
    }

    hKey[DB_KEY_LENGTH_BYTE] = 0;

    if (nKeyLen < DB_KEY_LENGTH_BYTE)
    {
        memcpy(hKey, pKey, nKeyLen); //先拷貝得到金鑰前面的部分
        j = DB_KEY_LENGTH_BYTE - nKeyLen;

        //補充金鑰後面的部分
        memset(hKey + nKeyLen, DB_KEY_PADDING, j);
    }
    else
    {
        //金鑰位數已經足夠,直接把金鑰取過來
        memcpy(hKey, pKey, DB_KEY_LENGTH_BYTE);
    }

    return hKey;
}

 

6.在splite3.c檔案的最後面新增以下程式碼

 

#ifdef SQLITE_HAS_CODEC
#include "sqlite3crypt.h"

// 得到頁所對應的編/解碼塊
static void * sqlite3pager_get_codecarg(struct Pager *pPager)
{
       return (pPager->xCodec) ? pPager->pCodec : 0;
}

// 設定頁所對應的編/解碼塊
// @Param pPager 要加密的頁
// @Param xCodec 加/解密回撥
// @Param xCodecFree 釋放頁對應的加/解密模組回撥
// @Param pCodecArg 加/解密模組
void sqlite3pager_set_codec(Pager *pPager, void *(*xCodec)(void*, void*, Pgno, int), void (*xCodecFree)(void*), void *pCodecArg)
{
       pPager->xCodec = xCodec;
       pPager->pCodec = pCodecArg;
       pPager->xCodecFree = xCodecFree;
}


// 實現Sqlite3介面
// ===============================================================================
// ===============================================================================
// 下面是編譯時提示缺少的函式 
// 這個函式不需要做任何處理,獲取金鑰的部分在下面 DeriveKey 函式裡實現
void sqlite3CodecGetKey(struct sqlite3* db, int nDB, void** Key, int* nKey) {
    return;
}

// 這個函式好像是 sqlite 3.3.17前不久才加的,以前版本的sqlite裡沒有看到這個函式
// 這個函式我還沒有搞清楚是做什麼的,它裡面什麼都不做直接返回,對加解密沒有影響
void sqlite3_activate_see(const char* right) {
    return;
}
 
 // 實現加密介面
int sqlite3_key(sqlite3 *db, const void *pKey, int nKey)
{
    return sqlite3CodecAttach(db, 0, pKey, nKey);
}
 
 // 實現解密介面
int sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey)
{
    Pgno nSkip;
     void *pPage;
     Pgno n;
       Btree *pbt = db->aDb[0].pBt;
       Pager *p = sqlite3BtreePager(pbt);
       LPCryptBlock pBlock = (LPCryptBlock) sqlite3pager_get_codecarg(p);
       unsigned char * hKey = DeriveKey(pKey, nKey);
       int rc = SQLITE_ERROR;
 
       if (!pBlock && !hKey)
              return SQLITE_OK;
 
       //重新加密一個資料庫,改變pager的寫金鑰, 讀金鑰依舊保留.
       if (!pBlock) //加密一個未加密的資料庫
       {
              pBlock = CreateCryptBlock(hKey, p->pageSize, NULL);
              pBlock->ReadKey = 0; // 原始資料庫未加密
              sqlite3pager_set_codec(sqlite3BtreePager(pbt), sqlite3Codec, sqlite3CodecFree, pBlock);
       }
       else // 改變已加密資料庫的寫金鑰
       {
              pBlock->WriteKey = hKey;
       }
 
       // 開始一個事務
       rc = sqlite3BtreeBeginTrans(pbt, 1);
       if (!rc)
       {
              // 用新金鑰重寫所有的頁到資料庫。
              //Pgno nPage = sqlite3PagerPagecount(p);
           int nPage = 0;
           sqlite3PagerPagecount(p, &nPage);
             
             nSkip = PAGER_MJ_PGNO(p);
              for (n = 1; rc == SQLITE_OK && n <= nPage; n++)
              {
                     if (n == nSkip)
                           continue;
 
                     rc = sqlite3PagerGet(p, n, (DbPage**)&pPage);
                     if (!rc)
                     {
                           rc = sqlite3PagerWrite((DbPage*)pPage);
                           sqlite3PagerUnref((DbPage*)pPage);
                     }
              }
       }
 
       // 如果成功,提交事務。
       if (!rc)
       {
              rc = sqlite3BtreeCommit(pbt);
       }
 
       // 如果失敗,回滾。
       if (rc)
       {
              sqlite3BtreeRollback(pbt, SQLITE_OK);
       }
 
       // 如果成功,銷燬先前的讀金鑰。並使讀金鑰等於當前的寫金鑰。
       if (!rc)
       {
              if (pBlock->ReadKey)
              {
                     sqlite3_free(pBlock->ReadKey);
              }
 
              pBlock->ReadKey = pBlock->WriteKey;
       }
       else // 如果失敗,銷燬當前的寫金鑰,並恢復為當前的讀金鑰。
       {
              if (pBlock->WriteKey)
              {
                     sqlite3_free(pBlock->WriteKey);
              }
              pBlock->WriteKey = pBlock->ReadKey;
       }
 
       // 如果讀金鑰和寫金鑰皆為空,就不需要再對頁進行編解碼。
       // 銷燬加密塊並移除頁的編解碼器
       if (!pBlock->ReadKey && !pBlock->WriteKey)
       {
              sqlite3pager_set_codec(p, NULL, NULL, NULL);
              sqlite3CodecFree(pBlock);
       }
 
       return rc;
}
 
// 實現附加金鑰到資料庫介面
// ================================================================================
int sqlite3CodecAttach(sqlite3 *db, int nDb, const void *pKey, int nKeyLen)
{
       int rc = SQLITE_ERROR;
       unsigned char* hKey = 0;
 
       //如果沒有指定密匙,可能標識用了主資料庫的加密或沒加密.
       if (!pKey || !nKeyLen)
       {
              if (!nDb)
              {
                     return SQLITE_OK; //主資料庫, 沒有指定金鑰所以沒有加密.
              }
              else //附加資料庫,使用主資料庫的金鑰.
              {
                     //獲取主資料庫的加密塊並複製金鑰給附加資料庫使用
                     LPCryptBlock pBlock = (LPCryptBlock) sqlite3pager_get_codecarg(sqlite3BtreePager(db->aDb[0].pBt));
 
                     if (!pBlock)
                           return SQLITE_OK; //主資料庫沒有加密
 
                     if (!pBlock->ReadKey)
                           return SQLITE_OK; //沒有加密
 
                     memcpy(pBlock->ReadKey, &hKey, 16);
              }
       }
       else //使用者提供了密碼,從中建立金鑰.
       {
              hKey = DeriveKey(pKey, nKeyLen);
       }
 
       //建立一個新的加密塊,並將解碼器指向新的附加資料庫.
       if (hKey)
       {
              LPCryptBlock pBlock = CreateCryptBlock(hKey, sqlite3BtreePager(db->aDb[nDb].pBt)->pageSize, NULL);
              sqlite3pager_set_codec(sqlite3BtreePager(db->aDb[nDb].pBt), sqlite3Codec, sqlite3CodecFree, pBlock);
              rc = SQLITE_OK;
       }
 
       return rc;
}

#endif //#ifdef SQLITE_HAS_CODEC

7.OK,程式碼部分完成了,但要使用Sqlite的加密前必須要預新增加SQLITE_HAS_CODEC這個巨集,怎麼加就不詳訴了

 

8.最後,測試程式碼

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern "C" {
#include "sqlite3.h"
};

#define  SQLITE3_STATIC

extern int sqlite3_key(sqlite3 *db, const void *pKey, int nKey);

static int _callback_exec(void * notused,int argc, char ** argv, char ** aszColName)
{
    int i;
    for ( i=0; i<argc; i++ )
    {
        printf( "%s = %s\r\n", aszColName[i], argv[i] == 0 ? "NUL" : argv[i] );
    }

    return 0;
}

int main(int argc, char * argv[])
{
    const char * sSQL;
    char * pErrMsg = 0;
    int ret = 0;
    sqlite3 * db = 0;

    //建立資料庫
    ret = sqlite3_open("encrypt.db", &db);

    //新增密碼
    ret = sqlite3_key( db, "mypass", strlen("mypass"));

    //在記憶體資料庫中建立表
    sSQL = "create table class(name varchar(20), student);";
    sqlite3_exec( db, sSQL, _callback_exec, 0, &pErrMsg );

    //插入資料
    sSQL = "insert into class values('mem_52911', 'zhaoyun');";
    sqlite3_exec( db, sSQL, _callback_exec, 0, &pErrMsg );

    //取得資料並顯示
    sSQL = "select * from class;";
    sqlite3_exec( db, sSQL, _callback_exec, 0, &pErrMsg );

    //關閉資料庫
    sqlite3_close(db);
    db = 0;

    return 0;
}

 

相關文章