zoukankan      html  css  js  c++  java
  • SQLite 3.7.13的加密解密(四)—— 挂接加密解密函数

    把crypt.c中实现的加密解密函数挂接到sqlite3.c中,并且实现前面编译提示的未实现的函数。在sqlite3.c的最后一行的后面,添加如下代码:
    #ifdef SQLITE_HAS_CODEC
    #include "crypt.h"
     
    /***
     加密结构
     ***/
    #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
     
    /*** 下面是编译时提示缺少的函数 ***/
    /** 这个函数不需要做任何处理,获取密钥的部分在下面 DeriveKey 函数里实现 **/
    void sqlite3CodecGetKey(sqlite3* db, int nDB, void** Key, int* nKey) {
           return;
    }
     
    /*被sqlite 和 sqlite3_key_interop 调用, 附加密钥到数据库.*/
    int sqlite3CodecAttach(sqlite3 *db, int nDb, const void *pKey, int nKeyLen);
     
    /**
     这个函数好像是 sqlite 3.3.17前不久才加的,以前版本的sqlite里没有看到这个函数
     这个函数我还没有搞清楚是做什么的,它里面什么都不做直接返回,对加解密没有影响
     **/
    void sqlite3_activate_see(const char* right) {
           return;
    }
     
    int sqlite3_key(sqlite3 *db, const void *pKey, int nKey);
    int sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey);
     
    /***
     下面是上面的函数的辅助处理函数
     ***/
    // 从用户提供的缓冲区中得到一个加密密钥
    // 用户提供的密钥可能位数上满足不了要求,使用这个函数来完成密钥扩展
    static unsigned char * DeriveKey(const void *pKey, int nKeyLen);
    //创建或更新一个页的加密算法索引.此函数会申请缓冲区.

    static LPCryptBlock CreateCryptBlock(unsigned char* hKey, Pager *pager,

                  LPCryptBlock pExisting);
    //加密/解密函数, 被pager调用

    void * sqlite3Codec(void *pArg, unsigned char *data, Pgno nPageNum, intnMode);

    //设置密码函数
    int __stdcall sqlite3_key_interop(sqlite3 *db, const void *pKey, int nKeySize);
    // 修改密码函数
    int __stdcall sqlite3_rekey_interop(sqlite3 *db, const void *pKey, int nKeySize);
    //销毁一个加密块及相关的缓冲区,密钥.
    static void DestroyCryptBlock(LPCryptBlock pBlock);

    static void * sqlite3pager_get_codecarg(Pager *pPager);

    void sqlite3pager_set_codec(Pager *pPager,
                  void *(*xCodec)(void*, void*, Pgno, int), void *pCodecArg);
     
    //加密/解密函数, 被pager调用

    void * sqlite3Codec(void *pArg, unsigned char *data, Pgno nPageNum, intnMode) {

           LPCryptBlock pBlock = (LPCryptBlock) pArg;
           unsigned int dwPageSize = 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;
     

                  dwPageSize = pBlock->PageSize;

     

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

     

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

     

                  My_Encrypt_Func(data, dwPageSize, pBlock->ReadKey, DB_KEY_LENGTH_BYTE); /*调用我的加密函数*/

     
                  break;
           }
     
           return data;
    }
     
    //销毁一个加密块及相关的缓冲区,密钥.
    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);
    }
     

    static void * sqlite3pager_get_codecarg(Pager *pPager)

    {

           return (pPager->xCodec) ? pPager->pCodecArg : NULL;

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

           if (pKey == NULL || nKeyLen == 0)

           {
                  return NULL;
           }
     

           hKey = sqliteMalloc(DB_KEY_LENGTH_BYTE + 1);

           if (hKey == NULL)
           {
                  return NULL;
           }
     

           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;
    }
     
    //创建或更新一个页的加密算法索引.此函数会申请缓冲区.

    static LPCryptBlock CreateCryptBlock(unsigned char* hKey, Pager *pager,

                  LPCryptBlock pExisting)
     
    {
           LPCryptBlock pBlock;
     
           if (!pExisting) //创建新加密块
           {

                  pBlock = sqliteMalloc(sizeof(CryptBlock));

                  memset(pBlock, 0, sizeof(CryptBlock));
                  pBlock->ReadKey = hKey;
                  pBlock->WriteKey = hKey;
                  pBlock->PageSize = pager->pageSize;
                  pBlock->Data = (unsigned char*) sqliteMalloc(
                               pBlock->PageSize + CRYPT_OFFSET);
           }
           else //更新存在的加密块
           {

                  pBlock = pExisting;

     

                  if (pBlock->PageSize != pager->pageSize && !pBlock->Data) {

                         sqliteFree(pBlock->Data);
                         pBlock->PageSize = pager->pageSize;
     
                         pBlock->Data = (unsigned char*) sqliteMalloc(
                                      pBlock->PageSize + CRYPT_OFFSET);
                  }
           }
     
           memset(pBlock->Data, 0, pBlock->PageSize + CRYPT_OFFSET);
     
           return pBlock;
    }
     
    /*

     ** Set the codec for this pager

     */
    void sqlite3pager_set_codec(Pager *pPager, void *(*xCodec)(void*, void*, Pgno,int), void *pCodecArg)
    {

           pPager->xCodec = xCodec;

           pPager->pCodecArg = pCodecArg;

    }
     
    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);

    }
     
    /*被sqlite 和 sqlite3_key_interop 调用, 附加密钥到数据库.*/
    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), NULL);
     
                  sqlite3pager_set_codec(sqlite3BtreePager(db->aDb[nDb].pBt),

                               sqlite3Codec, pBlock);

     
                  rc = SQLITE_OK;
           }
     
           return rc;
    }
     
    // Changes the encryption key for an existing database.
    int __stdcall 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);

           unsigned char * 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; // 原始数据库未加密

                  sqlite3pager_set_codec(sqlite3BtreePager(pbt), sqlite3Codec, pBlock);

           }
           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)

           {

                  sqlite3pager_set_codec(p, NULLNULL);

                  DestroyCryptBlock(pBlock);
           }
     
           return rc;
    }
     
    /***
     下面是加密函数的主体
     ***/
    int __stdcall sqlite3_key_interop(sqlite3 *db, const void *pKey, int nKeySize)
    {

           return sqlite3CodecAttach(db, 0, pKey, nKeySize);

    }
     
    // 释放与一个页相关的加密块
    void sqlite3pager_free_codecarg(void *pArg)
    {
           if (pArg)
                  DestroyCryptBlock((LPCryptBlock) pArg);
    }
     
    #endif //#ifdef SQLITE_HAS_CODEC
     
    特别说明:
    DeriveKey 函数,这个函数是对密钥的扩展。比如,你要求密钥是128位,即是16字节,但是如果用户只输入 1个字节呢?2个字节呢?或输入50个字节呢?你得对密钥进行扩展,使之符合16字节的要求。

    DeriveKey 函数就是做这个扩展的。有人把接收到的密钥求md5,这也是一个办法,因为md5运算结果固定16字节,不论你有多少字符,最后就是16字节。这是md5算法的特点。但是我不想用md5,因为还得为它添加包含一些 md5 的.c或.cpp文件。我不想这么做。我自己写了一个算法来扩展密钥,很简单的算法。当然,你也可以使用你的扩展方法,也而可以使用 md5 算法。只要修改 DeriveKey 函数就可以了。

    在 DeriveKey 函数里,只管申请空间构造所需要的密钥,不需要释放,因为在另一个函数里有释放过程,而那个函数会在数据库关闭时被调用。参考上面 DeriveKey 函数来申请内存。

    本文出自 “rainman” 博客,请务必保留此出处http://lancelot.blog.51cto.com/393579/940813


  • 相关阅读:
    NOI Online 2020 提高组游记
    【HDU5840】This world need more Zhu
    CSP-S 2019 AFO记
    防错笔记
    关于Blog
    题解 【UER #6】逃跑
    动态规划杂题选记
    有趣计数题选做
    题解 [POI2012] Leveling Ground
    xioa han 带画家!
  • 原文地址:https://www.cnblogs.com/iapp/p/3631762.html
Copyright © 2011-2022 走看看