SQLCipher核心思想

2023-11-18

加密原理

+--------------------------------------+----------------+--------------------+
|            page data                 |     iv         |     hmac           |
+--------------------------------------+----------------+--------------------+

iv是一段随机数,可以保证每一页的iv值都不一样。和page data一起作用,用于生成hmac值。
sizeof(page data) = page sz - reserved sz
hmac是一段校验和,可以用来检查page data是否损坏。
sizeof(iv + hmac) = reserved sz

加密实现是通过派生后的key+盐值,对原始page加密,生成加密数据。然后通过iv,对加密数据生成校验和hmac值。

Set key / Attach

SQLCipher的实现方式是在读、写page的时候实现的加、解密。通过pragma key和attach流程中可以分别设置key,在这一过程中只是注册了加解密的实现算法、保存了key。在后面的第一次操作中才开始派生key。

pragma key的入口函数是sqlite3Pragma->sqlite3_key_v2,attach的入口函数是attachFunc。

在这两个函数内,实际上都是执行sqlite3CodecAttach操作

int sqlite3CodecAttach(
    sqlite3* db,
    int nDb,            // 数据库数组db->aDb的索引,前两个分别是main和temp,后面的是attach的
    const void *zKey,   // 密文或者密钥的地址
    int nKey            // 密钥的长度,如果是密文,这里是-1
);

在这个函数里。主要执行了以下动作:

  • sqlcipher_activate
    注册了全局的sqlcipher_provider *default_provider。这个里面主要存了一些加解密算法的实现函数的指针。可以通过宏选择加解密算法,这些算法都是利用第三方的开源加密库。比如SQLCIPHER_CRYPTO_CC,可以选择common crypto算法,实现放在crypto_cc.c里。

  • sqlcipher_codec_ctx_init
    这个函数主要填充了codec_ctx对象,这个对象对应于一个数据库。这里面的一些关于加密的参数都先设置成了默认值,密文(或密钥)填充进两个cipher_ctx——read_ctx和write_ctx里。

    typedef struct {
      int derive_key;               // 是否需要派生key。设置原始key后置位,派生key后取消置位
      int pass_sz;                  // 原始key的size。因为有可能是blob,所以需要size
      unsigned char *key;           // 派生后的key。可能是用户设置的HEX key转换成二进制得到,或者由用户设置的密文和盐值经过kdf_iter次派生得到
      unsigned char *hmac_key;      // 由key和hmac_kdf_salt经过fast_kdf_iter次派生得到
      unsigned char *pass;          // 配置的原始key,可能是密文、x'HEX key'、x'HEX key + HEX kdf_salt'
      char *keyspec;                // x'HEX key + HEX kdf_salt'
    } cipher_ctx;
    
    typedef struct {
      int store_pass;               // 是否要保存原始key。如果设成false,应该在派生key后清空原始key
      int kdf_iter;                 // key的派生次数
      int fast_kdf_iter;            // hmac_key的派生次数
      int kdf_salt_sz;
      int key_sz;
      int iv_sz;
      int block_sz;
      int page_sz;
      int keyspec_sz;
      int reserve_sz;
      int hmac_sz;
      int plaintext_header_sz;
      int hmac_algorithm;
      int kdf_algorithm;
      unsigned int skip_read_hmac;
      unsigned int need_kdf_salt;   // 是否需要取kdf_salt。默认置位,取到kdf_salt后取消置位
      unsigned int flags;           // 默认值DEFAULT_CIPHER_FLAGS,标志是否要hmac校验、大小端
      unsigned char *kdf_salt;      // 生成key的盐值。优先使用用户配置的,其次从文件头取,取不到用随机数
      unsigned char *hmac_kdf_salt; // kdf_salt的每字节 XOR hmac_salt_mask
      unsigned char *buffer;        // 预申请的一段内存,用于加、解密的缓存区,大小为page_sz
      Btree *pBt;
      cipher_ctx *read_ctx;         // 读page用的密钥配置
      cipher_ctx *write_ctx;        // 写page用的密钥配置
      sqlcipher_provider *provider; // 默认的provider,算法可以通过宏配置
      void *provider_ctx;
    } codec_ctx;
    
  • sqlite3PagerSetCodec
    这个函数把加解密的函数注册进了pager里面。这些函数后面在读写page的时候会用到。

    db->aDb[nDb]->pBt->pBt->pPager->xCodec;     // sqlite3Codec
    db->aDb[nDb]->pBt->pBt->pPager->xCodecFree; // sqlite3FreeCodecArg
    db->aDb[nDb]->pBt->pBt->pPager->pCodec;     // codec_ctx
    

    最重要的是sqlite3Codec这个函数,这个函数实现了key的派生,对页面的加密、解密。代码中没有直接调用过这个函数,而是把这个函数注册进pager,又把pager->xCodec封装成两个宏。CODEC1用于解密,CODEC2用于加密。

    # define CODEC1(P,D,N,X,E) \
        if( P->xCodec && P->xCodec(P->pCodec,D,N,X)==0 ){ E; }
    # define CODEC2(P,D,N,X,E,O) \
        if( P->xCodec==0 ){ O=(char*)D; }else \
        if( (O=(char*)(P->xCodec(P->pCodec,D,N,X)))==0 ){ E; }
    

    P代表pager,D代表输入数据pData(如果是解密操作,也是输出数据),N代表pageno,X代表操作码CODEC_READ_OP或CODEC_WRITE_OP或CODEC_JOURNAL_OP,E代表执行出错时的动作,O代表加密后的输出数据。

    sqlite3Codec执行以下动作:

    • sqlcipher_codec_key_derive
      派生key。检查derive_key标志位,如果置位,执行sqlcipher_cipher_ctx_key_derive,派生key,然后清除derive_key标志位。先read_ctx后write_ctx,如果两个ctx里的原始key一样,派生一个就行,另一个copy过去。然后检查store_pass标志位,如果未置位,需要清空两个原始key。
    • 根据不同的操作码分别做处理。如果是CODEC_READ_OP,利用read_ctx执行解密操作,page数据pData解密后,缓存到buffer里,然后再copy buffer数据到pData中,返回pData;如果是CODEC_WRITE_OP,利用write_ctx执行加密操作,pData数据加密后,缓存到buffer里,返回buffer;如果是CODEC_JOURNAL_OP,也是执行加密操作,不过是利用read_ctx使用原始key加密日志数据,这是为了防止rekey事务失败,需要rollback的场景,此时需要从journal文件中读取数据。具体加解密、hmac校验的动作在sqlcipher_page_cipher里实现。

Rekey

Rekey是对一个加密数据库更改密钥的操作。

在set key的流程中,有一个生成reat_ctx和write_ctx的过程sqlcipher_codec_ctx_init。这两个对象绝大多数情况都是相同的,只有在rekey过程中不同。

在rekey过程中,正是利用了这两个对象实现了更改密文的操作。可以分为三步:

  1. 设置新key到write_ctx;
  2. 逐页读出后重新写入。读的过程中使用read_ctx解密,写的过程中使用write_ctx加密;
  3. 复制write_ctx到read_ctx中。

这三步完成后,rekey操作成功。老的数据都已经转换成新key加密的了,再写入新数据也是用新key加密的。

Export

Export可以实现将一个加密数据库导出到一个明文数据库、将一个明文数据库导出到一个加密数据库,将一个加密数据库导出到一个不同key的加密数据库中。export过程的内部实现方法是sqlcipher_exportFunc。该函数内实际上是利用原生的sqlite语句实现了导出。

下面的语句执行后可以得到 CREATE table/index 或者 INSERT INTO SELECT 语句,然后再执行查询出的语句即可实现复制。

-- CREATE table/index/unique index ...
SELECT sql FROM source.sqlite_schema WHERE type='table' AND name!='sqlite_sequence' AND rootpage>0;
SELECT sql FROM source.sqlite_schema WHERE sql LIKE 'CREATE INDEX %%';
SELECT sql FROM source.sqlite_schema WHERE sql LIKE 'CREATE UNIQUE INDEX %%';

-- INSERT INTO target.xxx SELECT * FROM main.xxx
SELECT 'INSERT INTO target.' || quote(name) || '
          SELECT * FROM source.' || quote(name) || ';'
FROM source.sqlite_schema
WHERE type = 'table' AND name!='sqlite_sequence' AND rootpage>0;

-- INSERT INTO target.sqlite_sequence SELECT * FROM main.sqlite_sequence
SELECT 'INSERT INTO target.' || quote(name) || '
          SELECT * FROM source.' || quote(name) || ';'
FROM target.sqlite_schema
WHERE name=='sqlite_sequence';

下面的语句可以直接执行。

INSERT INTO target.sqlite_schema
  SELECT type, name, tbl_name, rootpage, sql
  FROM source.sqlite_schema
  WHERE type='view' OR type='trigger' OR (type='table' AND rootpage=0)";

这两部分sql执行完,实际上是实现了export的功能。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SQLCipher核心思想 的相关文章

  • vue框架如何将侧边栏完全隐藏

    vue框架如何将侧边栏完全隐藏 如何将vue的左侧边栏在缩进的时候完全隐藏呢 效果图如下 找到目录src style sidebar scss 然后搜索 hideSidebar可以搜出两个 不要慌 下面的时手机端的 我们拉到上面的 hide

随机推荐

  • 【算法日志】哈希表应用:set和map容器,哈希表的使用(day5)

    代码随想录60day 链表 day4 链表 day3 目录 前言 一 三种哈希结构 数组 散列技术 哈希思想 哈希碰撞 set 集合 map 映射 二 哈希表的一些应用 总结 前言 哈希表 也叫散列表 是一种较为常用的数据结构 我们常用的数
  • 1486. XOR Operation in an Array

    class Solution public int xorOperation int n int start int nret start for int m 1 m lt n m nret nret start 2 m return nr
  • 编译原理(4)LR(0)语法分析程序设计(Python实现)

    1 实验要求 1 已知文法G S 0 S E 1 E aA 2 E bB 3 A cA 4 A d 5 B cB 6 B d 手工建立文法G S 的LR 0 的项目集规范族DFA和LR 0 分析表 2 根据清华大学版 编译原理 第3版 教材
  • Jfugue编程概要

    转自 http www sudu cn info html edu java 20060912 304274 html JFugue是个用于音乐作曲的Java API 和其他的音乐API不同 他能够让你用数据字符串来指定音符 乐器 和弦 及
  • java sql 查询中的转义序列不对_在 JDBC 中使用 SQL 转义序列 - SQL Server

    使用 SQL 转义序列Using SQL escape sequences 08 12 2019 本文内容 按照 JDBC API 的定义 Microsoft JDBC Driver for SQL ServerMicrosoft JDBC
  • 20天零基础自学Python

    大家好 我是宁一 Python 数字数据类型是用来存储数值的 是我们从小学就开始接触的老朋友了 也是python中最基础的数据类型 1 Number 数据类型 python3的 Number 数据类型包括 int 整数 float 浮点数
  • B站化播放量为播放时长,是谁的狂欢?

    6月26日晚 B站举办了14周年庆典晚会 在晚会上 除了周深 美依礼芽同框献唱受到关注 B站董事长兼CEO陈睿的演讲内容同样值得深思 一来 陈睿提到 要将目前B站视频前台显示的播放量数据从次数改为分钟数 计划未来几周 将完成产品更新 二来
  • 堆栈内存和闭包

    思维导图 堆栈内存小科普 1 js中的内存分为 堆内存和栈内存 堆内存 只要用来存储引用数据类型的值 对象存的是键值对 函数存的是字符串 栈内存 供js运行的环境 函数执行 存基本数据类型的值 堆栈内存的释放问题 我们每次给变量存值或者执行
  • 程序员秋招最全Java面试题及答案整理(2023最新版)

    前言 大家好 最近一个月 花了不少时间 给大家整理了一套 2023 的技术面试资料 包括各大厂最新面试题以及面经解析涉及JVM Mysql 并发 Spring Mybatis Redis RocketMQ Kafka Zookeeper N
  • 【C刷题】day1

    一 选择题 1 正确的输出结果是 int x 5 y 7 void swap int z z x x y y z int main int x 3 y 8 swap printf d d n x y return 0 答案 3 8 解析 考
  • 怎样将excel文件导入mysql中

    1 整理好excel表中的字段 2 在Navicat中创建表 如果导入的是一个追加的表 则无需创建新表 CREATE TABLE orderinfo orderid VARCHAR 10 NULL 订单 id 主键 userid INT 1
  • 华为OD机试2023年最新题库(JAVA、Python、C++)

    我是一名软件开发培训机构老师 我的学生已经有上百人通过了华为OD机试 学生们每次考完试 会把题目拿出来一起交流分享 重要 5 11月份考的都是OD统一考试 B卷 2023年5月份题库已经更新为OD统一考试 B卷 题库由三部分组成 1 202
  • 【H5】 svg内text、image、path标签的使用

    H5 svg内text image path标签的使用 text标签 div style width 500px height 500px border 2px solid pink margin 50px auto 0 div
  • XML中约束文档的引用和书写

    在XML中定义了一套规则 来对文档内容进行约束 这叫做XML约束 常用的俩种约束语言 DTD约束 Schema约束 XML文档中可以引入多个约束文档 为了防止出现不同含义的同名名称冲突 所以 所以可以XML提供了名称空间 1 DTD语法 D
  • 【HTML】列表标签、表格标签、块级标签、表单标签

    文章目录 一 列表标签 1 无序列表 2 无序列表 3 定义列表 项目列表 二 表格标签 1 表格整体架构 2 表格的标签介绍 3 table标签的属性 4 tr标签的属性 5 th td标签的属性 6 跨行 跨列的表格 三 块级标签 1
  • vue猜数字游戏

    div p msg p div
  • node.js JSON对象和string的相互转化

    JSON stringify obj 将JSON转为字符串 var json aa sdddssd bb 892394829342394792399 23894723984729374932874 cc 11111111111111 gt
  • html5期末知识点归纳总结,web期末考试知识点

    题型及知识点 一 知识点 上课内容全覆盖 除补充的html5和css3的内容 常用的html标记及属性 弄清楚哪些是块元素 哪些是行内元素 特殊字符标记 p40 Css属性 i 字体 font size font family font w
  • 蓝桥杯JAVA B组 2020(1)第五题 排序

    一 题目描述 小蓝最近学习了一些排序算法 其中冒泡排序让他印象深刻 在冒泡排序中 每次只能交换相邻的两个元素 小蓝发现 如果对一个字符串中的字符排序 只允许交换相邻的两个字符 则在所有可能的排序方案中 冒泡排序的总交换次数是最少的 例如 对
  • SQLCipher核心思想

    加密原理 page data iv hmac iv是一段随机数 可以保证每一页的iv值都不一样 和page data一起作用 用于生成hmac值 sizeof page data p