⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 xxtea.c

📁 sqlite数据库的源代码, 例子是在windows上使用,不过可以移值到各种平台上使用
💻 C
字号:
/*
    为SQLite加入了加密功能

    Write By 夕霞孤雁   leadworld@gmail.com
            
            2007-05-10
*/


#include "sqlite3.c"

#ifndef SQLITE_OMIT_DISKIO
#ifdef SQLITE_HAS_CODEC

#include "md5.h"


#define CRYPT_OFFSET 8
#define BYTE unsigned char


typedef struct _CryptBlock
{
    BYTE*     ReadKey;     // 读数据库和写入事务的密钥
    BYTE*     WriteKey;    // 写入数据库的密钥
    int       PageSize;    // 页的大小
    BYTE*     Data;
} CryptBlock, *LPCryptBlock;



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 ;
} /* Signal n=0,1,-1 */


void * sqlite3pager_get_codecarg(Pager *pPager)
{
    return (pPager->xCodec) ? pPager->pCodecArg: NULL;
}

void sqlite3_activate_see(const char *info)
{
}

//创建或更新一个页的加密算法索引.此函数会申请缓冲区.
static LPCryptBlock CreateCryptBlock(BYTE* hKey, Pager *pager, LPCryptBlock pExisting)
{
    LPCryptBlock pBlock;

    if (!pExisting) //创建新加密块
    {
        pBlock = sqliteMalloc(sizeof(CryptBlock));
        ZeroMemory(pBlock, sizeof(CryptBlock));
        pBlock->ReadKey = hKey;
        pBlock->WriteKey = hKey;
        pBlock->PageSize = pager->pageSize;
        pBlock->Data = (BYTE*)sqliteMalloc(pBlock->PageSize + CRYPT_OFFSET);
    }
    else //更新存在的加密块
    {
        pBlock = pExisting;
        if ( pBlock->PageSize != pager->pageSize && !pBlock->Data){
            sqliteFree(pBlock->Data);
            pBlock->PageSize = pager->pageSize;
            pBlock->Data = (BYTE*)sqliteMalloc(pBlock->PageSize + CRYPT_OFFSET);
        }
    }


    ZeroMemory(pBlock->Data, pBlock->PageSize + CRYPT_OFFSET);

    return pBlock;
}

//销毁一个加密块及相关的缓冲区,密钥.
static void DestroyCryptBlock(LPCryptBlock pBlock)
{
    //销毁读密钥.
    if (pBlock->ReadKey){
        sqliteFree(pBlock->ReadKey);
    }

    //如果写密钥存在并且不等于读密钥,也销毁.
    if (pBlock->WriteKey && pBlock->WriteKey != pBlock->ReadKey){
        sqliteFree(pBlock->WriteKey);
    }

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

    //释放加密块.
    sqliteFree(pBlock);
}

//加密/解密函数, 被pager调用
void * sqlite3Codec(void *pArg, BYTE *data, Pgno nPageNum, int nMode)
{
    LPCryptBlock pBlock = (LPCryptBlock)pArg;
    int len = 0;

    if (!pBlock) return data;

    // 确保pager的页长度和加密块的页长度相等.如果改变,就需要调整.
    if (nMode != 2)
    {
        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;

        CopyMemory(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;

        CopyMemory(pBlock->Data + CRYPT_OFFSET, data, pBlock->PageSize);
        data = pBlock->Data + CRYPT_OFFSET;


        len = pBlock->PageSize/4;
        xxtea(data, len, pBlock->ReadKey);
        break;
    }

    return data;
}

// 从用户提供的缓冲区中得到一个加密密钥
static BYTE * DeriveKey(const void *pKey, int nKeyLen)
{
    BYTE *  hKey = NULL;
    BYTE *  digest = NULL;
    MD5_CTX md5ctx;


    if ((pKey == NULL) || (nKeyLen <1 ))
    {
        return NULL;
    }


    digest = sqliteMalloc(16);

    MD5Init(&md5ctx);

    MD5Update(&md5ctx, (unsigned char*)pKey, nKeyLen);
    MD5Final(digest, &md5ctx);

    hKey = digest;
    return hKey;
}

//被sqlite 和 sqlite3_key_interop 调用, 附加密钥到数据库.
int sqlite3CodecAttach(sqlite3 *db, int nDb, const void *pKey, int nKeyLen)
{
    int rc = SQLITE_ERROR;
    BYTE* 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), NULL);
        sqlite3PagerSetCodec(sqlite3BtreePager(db->aDb[nDb].pBt), sqlite3Codec, pBlock);
        db->aDb[nDb].pAux = pBlock;
        db->aDb[nDb].xFreeAux = DestroyCryptBlock;

        rc = SQLITE_OK;
    }
    return rc;
}

// 释放与一个页相关的加密块
void sqlite3pager_free_codecarg(void *pArg)
{
    if (pArg)
        DestroyCryptBlock((LPCryptBlock)pArg);
}

//不保存用户的原始密码.返回NULL.
void sqlite3CodecGetKey(sqlite3 *db, int nDb, void **ppKey, int *pnKeyLen)
{
    *ppKey = NULL;
    *pnKeyLen = 0;
}

//密钥并不保留到临时空间,仅保存于主数据库.
int sqlite3_key_interop(sqlite3 *db, const void *pKey, int nKeySize)
{
    return sqlite3CodecAttach(db, 0, pKey, nKeySize);
}

//改变已有数据库的加密密钥
int sqlite3_rekey_interop(sqlite3 *db, const void *pKey, int nKeySize)
{
    Btree *pbt = db->aDb[0].pBt;
    Pager *p = sqlite3BtreePager(pbt);
    LPCryptBlock pBlock = (LPCryptBlock)sqlite3pager_get_codecarg(p);
    BYTE * hKey = DeriveKey(pKey, nKeySize);
    int rc = SQLITE_ERROR;


    if (!pBlock && !hKey) return SQLITE_OK; 

    //重新加密一个数据库,改变pager的写密钥, 读密钥依旧保留.
    if (!pBlock) //加密一个未加密的数据库
    {
        pBlock = CreateCryptBlock(hKey, p, NULL);
        pBlock->ReadKey = 0; // 原始数据库未加密
        sqlite3PagerSetCodec(sqlite3BtreePager(pbt), sqlite3Codec, pBlock);
        db->aDb[0].pAux = pBlock;
        db->aDb[0].xFreeAux = DestroyCryptBlock;
    }
    else // 改变已加密数据库的写密钥
    {
        pBlock->WriteKey = hKey;
    }

    // 开始一个事务
    rc = sqlite3BtreeBeginTrans(pbt, 1);

    if (!rc)
    {
        // 用新密钥重写所有的页到数据库。
        Pgno nPage = sqlite3PagerPagecount(p);
        Pgno nSkip = PAGER_MJ_PGNO(p);
        void *pPage;
        Pgno n;

        for(n = 1; rc == SQLITE_OK && n <= nPage; n ++)
        {
            if (n == nSkip) continue;
            rc = sqlite3PagerGet(p, n, &pPage);
            if(!rc)
            {
                rc = sqlite3PagerWrite(pPage);
                sqlite3PagerUnref(pPage);
            }
        }
    }

    // 如果成功,提交事务。
    if (!rc)
    {
        rc = sqlite3BtreeCommit(pbt);
    }

    // 如果失败,回滚。
    if (rc)
    {
        sqlite3BtreeRollback(pbt);
    }



    // 如果成功,销毁先前的读密钥。并使读密钥等于当前的写密钥。
    if (!rc)
    {
        if (pBlock->ReadKey)
        {
            sqliteFree(pBlock->ReadKey);
        }
        pBlock->ReadKey = pBlock->WriteKey;
    }
    else// 如果失败,销毁当前的写密钥,并恢复为当前的读密钥。
    {
        if (pBlock->WriteKey)
        {
            sqliteFree(pBlock->WriteKey);
        }
        pBlock->WriteKey = pBlock->ReadKey;
    }



    // 如果读密钥和写密钥皆为空,就不需要再对页进行编解码。
    // 销毁加密块并移除页的编解码器
    if (!pBlock->ReadKey && !pBlock->WriteKey)
    {
        sqlite3PagerSetCodec(p, NULL, NULL);
        db->aDb[0].pAux = NULL;
        db->aDb[0].xFreeAux = NULL;
        DestroyCryptBlock(pBlock);
    }

    return rc;
}

int sqlite3_key(sqlite3 *db, const void *pKey, int nKey)
{
    return sqlite3_key_interop(db, pKey, nKey);
}

int sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey)
{
    return sqlite3_rekey_interop(db, pKey, nKey);
}

#endif // SQLITE_HAS_CODEC

#endif // SQLITE_OMIT_DISKIO

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -