SQLCipher
SQLCipher 是一个基于 SQLite 数据库库的独立分支,它添加了对数据库文件和其它安全特性的 256 位 AES 加密,例如
- 即时加密
- 篡改检测
- 内存清理
- 强健的密钥派生
SQLCipher 基于 SQLite,并且定期整合稳定的上游发布特性。虽然 SQLCipher 作为源代码树的一个单独版本进行维护,但该项目尽量最小化对核心 SQLite 代码的更改。
SQLCipher 由 Zetetic,LLC 维护,并在官方 SQLCipher 网站 上提供更多信息和相关文档。
特性
- 快速性能,许多操作中的加密开销仅为 5-15%
- 数据库文件中的所有 100% 数据都进行了加密
- 良好的安全实践(CBC 模式,HMAC,密钥派生)
- 零配置和应用程序级加密
- 由同行评审的 OpenSSL 加密库提供的算法。
- 可配置的加密提供程序
兼容性
SQLCipher在同一主版本号内保持数据库格式兼容性,因此任何平台的程序都可以打开由其他程序创建的数据库,前提是它们之间的SQLCipher主版本号相同。然而,主版本更新(例如从3.x更新到4.x)通常包括对默认设置的更改。这意味着最新的主版本号的SQLCipher将无法默认打开由较旧版本创建的数据库。例如,SQLCipher 4引入了许多性能和安全增强功能。新的默认算法、增加的KDF迭代次数和更大的页面大小意味着SQLCipher 4将无法默认打开由SQLCipher 1.x、2.x或3.x创建的数据库。相反,应用程序要么必须将旧数据库迁移到新的格式,要么启用特殊的向后兼容模式。可用的选项在SQLCipher的升级文档中描述。
SQLCipher也与标准SQLite数据库兼容。当未提供密钥时,SQLCipher将表现得就像标准SQLite库一样。还可以使用ATTACH和sqlcipher_export()便捷函数将明文数据库(标准SQLite)转换为加密的SQLCipher数据库。
贡献
SQLCipher团队欢迎对核心库的贡献。所有贡献,包括拉取请求和补丁,应基于prerelease
分支,并附有贡献协议。我们强烈鼓励在开发之前和提交之前就拟议的更改进行讨论。
编译
构建 SQLCipher 与从源代码编译常规版本的 SQLite 类似,但有几个小的例外
- 您必须定义
SQLITE_HAS_CODEC
并选择SQLITE_TEMP_STORE=2
或SQLITE_TEMP_STORE=3
- 您需要链接到一个支持加密的库提供商(OpenSSL、LibTomCrypt、CommonCrypto/Security.framework 或 NSS)
以下示例演示了如何链接到 OpenSSL,这是大多数类 Unix 系统上可用的一种提供商。
示例 1. 静态链接(将 /opt/local/lib 替换为 libcrypto.a 的路径)。注意,在这个示例中,--enable-tempstore=yes
为构建设置 SQLITE_TEMP_STORE=2
。
$ ./configure --enable-tempstore=yes CFLAGS="-DSQLITE_HAS_CODEC" \
LDFLAGS="/opt/local/lib/libcrypto.a"
$ make
示例 2. 动态链接
$ ./configure --enable-tempstore=yes CFLAGS="-DSQLITE_HAS_CODEC" \
LDFLAGS="-lcrypto"
$ make
测试
在使用 SQLCipher 时,完整的 SQLite 测试套件无法成功完成。在某些情况下,加密会干扰需要访问数据库文件数据或 SQLCipher 不支持的特性的低级测试。那些旨在支持加密的测试是为非 SQLCipher 实现准备的。此外,由于 SQLite 测试并不总是隔离的,如果某个测试失败,可能会在后续步骤中触发其他失败的级联效应。
因此,SQLCipher 软件包包含它自己的独立测试,这些测试练习并验证了 SQLCipher 扩展的核心功能。此测试套件旨在提供对 SQLCipher 内部逻辑的简略验证;它不会对 SQLite 数据库系统进行全面的测试,也不会在特定平台上验证功能。由于 SQLCipher 基于稳定的上游 SQLite 构建,因此假定核心 SQLite 库代码正常运行(SQLite 核心在 SQLCipher 中几乎未受影响)。因此,额外的 SQLCipher 特定测试提供了必要的验证,表明库在启用安全性功能时按预期运作。
要运行 SQLCipher 特定测试,请按以下说明进行配置,然后运行以下代码以执行测试并接收结果报告
$ ./configure --enable-tempstore=yes --enable-fts5 CFLAGS="-DSQLITE_HAS_CODEC -DSQLCIPHER_TEST" \
LDFLAGS="-lcrypto"
$ make testfixture
$ ./testfixture test/sqlcipher.test
加密数据库
要使用 SQL 接口为数据库指定加密密码,请使用 PRAGMA。您输入的密码将经过 PBKDF2 密钥推导以获取数据库的加密密钥。
PRAGMA key = 'passphrase';
或者,您可以使用 blob 文字指定确切的字节数据序列。如果您使用此方法,您有责任确保提供的数据是 64 个字符的十六进制字符串,它将被直接转换为 32 字节(256 位)的密钥数据,而无需密钥推导。
PRAGMA key = "x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'";
要程序化地加密数据库,您可以使用 sqlite3_key
函数。在 pKey
中提供的数据将根据与 PRAGMA key
相同的规则转换为加密密钥。
int sqlite3_key(sqlite3 *db, const void *pKey, int nKey);
PRAGMA key
或 sqlite3_key
应该在打开数据库时作为第一个操作调用。
更改数据库密钥
要更改现有数据库的加密密码,可以在提供正确的数据库密码后使用 rekey PRAGMA;
PRAGMA key = 'passphrase'; -- start with the existing database passphrase
PRAGMA rekey = 'new-passphrase'; -- rekey will reencrypt with the new passphrase
可以使用十六进制 rekey PRAGMA 重新加密到特定的二进制值
PRAGMA rekey = "x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'";
这可以通过使用 sqlite3_rekey 进行编程实现
sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey)
支持
完整文档(设计、API、平台、用法)的主要来源是 SQLCipher 网站
https://www.zetetic.net/sqlcipher/documentation
支持和讨论的主要途径是 SQLCipher 讨论网站
https://discuss.zetetic.net/c/sqlcipher
有关使用 SQLCipher 的问题或支持问题应在 GitHub Issue 跟踪器中输入
https://github.com/sqlcipher/sqlcipher/issues
请勿将问题、支持问题或其他问题发布在关于 SQLCipher 的博客文章中,因为我们不经常监控它们。
如果您在自己的软件中使用 SQLCipher,请通过以下链接告知我们:[email protected]!
社区版开源许可
版权(c)2020,ZETETIC LLC 所有权利保留。
在不修改或修改的前提下,允许以源代码和二进制形式重新分发和使用,前提是满足以下条件:* 源代码的重新分发必须保留上述版权声明、本条件列表和以下免责声明。* 二进制形式的重新分发必须在随同分发的文档或其他材料中重新产生上述版权声明、本条件列表和以下免责声明。* ZETETIC LLC 及其贡献者的名称不得未经事先书面许可用于认可或推广从本软件派生出的产品。
本软件由 ZETETIC LLC 提供为“现状”,并不作任何明确或隐含的保证,包括但不限于对适销性和特定用途的隐含保证。在任何情况下,ZETETIC LLC 不对任何直接、间接、偶然、特殊、示范性的或后果性的损害(包括但不限于替换货物或服务的采购;使用、数据或利润的丧失;或业务中断)承担责任,无论其产生原因和责任理论,即使被告知可能发生此类损害。
SQLite README.md 开始
SQLite 源代码仓库
此仓库包含 SQLite 数据库引擎 的完整源代码。还包括一些测试脚本,但许多其他测试脚本和大部分文档是分开管理的。
版本控制
SQLite 源代码使用 Fossil 进行管理,Fossil 是一个专门设计和编写的分布式版本控制系统,专为支持 SQLite 开发而设计。Fossil 仓库包含してる。
如果您在GitHub或其他Git仓库或服务上阅读此内容,那么您看到的是镜像。Git镜像中提交记录和其他其它对象的名称与官方名称不同。官方提交记录名称位于授权镜像提交评论的页脚中。官方提交名称同样可以在树根目录下的manifest.uuid
文件中找到。在沟通SQLite提交时,始终使用官方名称而不是Git名称。
如果您从次要来源拉取SQLite源代码并希望验证其完整性,下面的验证代码真实性部分提供了如何进行的提示。
获取代码
如果您不打算使用Fossil,可以按照以下方式下载tar包或ZIP档案或SQLite档案
-
对于其他提交,请在前一个列表项的URL中用适当的分支名称或标记或哈希前缀替换“发布”。或者浏览时间线以定位所需的提交,点击其信息页面链接,然后点击信息页面上的“tar包”或“ZIP档案”链接。
如果确实想使用Fossil检出源代码树,请首先安装版本2.0或更高版本的Fossil。(源tar包和预编译的二进制文件可在此处获取。Fossil是一款独立程序。安装时,只需下载或构建单个可执行文件,并将其放置在您的$PATH目录中。)然后运行以下命令
mkdir -p ~/sqlite ~/Fossils
cd ~/sqlite
fossil clone https://www.sqlite.org/src ~/Fossils/sqlite.fossil
fossil open ~/Fossils/sqlite.fossil
使用上述步骤设置好仓库后,您可以使用以下命令更新到最新版本
fossil update trunk ;# latest trunk check-in
fossil update release ;# latest official release
或者输入“fossil ui”以获取基于网络的用户界面。
为类Unix系统编译
首先创建一个目录用于放置构建产品。建议将构建目录与源目录分开,但这不是必需的。进入构建目录,然后在源树的根目录下运行 configure 脚本。之后运行 "make"。
例如:
tar xzf sqlite.tar.gz ;# Unpack the source tree into "sqlite"
mkdir bld ;# Build will occur in a sibling directory
cd bld ;# Change to the build directory
../sqlite/configure ;# Run the configure script
make ;# Run the makefile.
make sqlite3.c ;# Build the "amalgamation" source file
make test ;# Run some tests (requires Tcl)
请参阅 makefile 以了解更多目标。
configure 脚本使用了 autoconf 2.61 和 libtool。如果配置脚本对您不起作用,您可以在源树的顶层目录找到一个名为 "Makefile.linux-gcc" 的通用 makefile,您可以将它复制并编辑以适应您的需求。通用 makefile 中的注释显示了需要进行的更改。
用于 Windows 系统的 MSVC
在 Windows 上,所有适用的构建产品均可以用 MSVC 进行编译。首先打开与期望的编译器版本相关的命令提示符窗口(例如,“VS2013 开发者命令提示符”)。接下来,使用提供的 "Makefile.msc" 和 NMAKE 构建受支持的某个目标。
例如,从名为 "sqlite" 的源子树的父目录中:
mkdir bld
cd bld
nmake /f ..\sqlite\Makefile.msc TOP=..\sqlite
nmake /f ..\sqlite\Makefile.msc sqlite3.c TOP=..\sqlite
nmake /f ..\sqlite\Makefile.msc sqlite3.dll TOP=..\sqlite
nmake /f ..\sqlite\Makefile.msc sqlite3.exe TOP=..\sqlite
nmake /f ..\sqlite\Makefile.msc test TOP=..\sqlite
NMAKE 命令行可以设置几个构建选项。例如,为了构建适用于 WinRT,只需将 "FOR_WINRT=1" 参数添加到上面的 "sqlite3.dll" 命令行。在调试 SQLite 代码时,建议将 "DEBUG=1" 参数添加到上述任意一个命令行中。
SQLite 运行时不需要 Tcl,但 makefile(包括 MSVC 的 makefile)需要 Tcl 安装。SQLite 包含大量生成的代码,而 Tcl 用来生成其中很大一部分。
源码巡礼
大多数核心源文件位于 src/ 子目录中。该 src/ 文件夹还包含用于构建 "testfixture" 测试框架的文件。用于 "testfixture" 的源文件名称都以 "test" 开头。《src/》还包含 "shell.c" 文件,这是 "sqlite3.exe" 命令行外壳 的主程序,以及 "tclsqlite.c" 文件,它实现了 SQLite 的 Tcl 绑定。(历史记录:SQLite 最初是一个 Tcl 扩展,后来才作为独立库出现。)
测试脚本和程序位于 test/ 子目录中。其他源代码库中也包含额外的测试代码。有关更多信息,请参阅 SQLite 如何进行测试。
ext/ 子目录包含扩展代码。全文搜索引擎位于 ext/fts3。R-Tree 引擎位于 ext/rtree。ext/misc 子目录包含了一些较小的单个文件扩展,例如 REGEXP 操作符。
tool/ 子目录包含用于构建生成的源代码文件、测试或生成辅助程序(例如 "sqlite3_analyzer(.exe)")的脚本和程序。
生成的源代码文件
SQLite使用的几个C语言源文件是从其他来源生成的,而不是由程序员手动输入。本节将总结这些自动生成的文件。要创建所有自动生成的文件,只需运行“make target_source”。"target_source" make目标将创建一个名为"tsrc/"的子目录,并填写构建SQLite所需的全部源文件,包括手动编辑的文件和自动生成的文件。
SQLite接口由 sqlite3.h 头文件定义,该文件由 src/sqlite.h.in、./manifest.uuid 和 ./VERSION 生成。位于 tool/mksqlite3h.tcl 的脚本进行转换。manifest.uuid文件包含特定检查入的SHA3哈希值,并用于生成SQLITE_SOURCE_ID宏。VERSION文件包含当前的SQLite版本号。sqlite3.h头文件实际上是src/sqlite.h.in的一个副本,其中在正确的位置插入了源-id和版本号。注意,sqlite3.h文件中的注释文本用于生成SQLite API文档的大部分内容。用于生成该文档的Tcl脚本位于单独的源代码库中。
SQL语言解析器是 parse.c,它由 src/parse.y 文件中的文法生成。将"parse.y"转换为"parse.c"由位于 ./doc/lemon.html 的 lemon LALR(1)解析器生成器完成。lemon的源代码位于 tool/lemon.c。Lemon使用 tool/lempar.c 文件作为生成其解析器的模板。Lemon还同时生成 parse.h 头文件,在该同时它还生成 parse.c。
opcodes.h 头文件包含定义 "VDBE" 虚拟机中操作码相应的宏。opcodes.h文件通过扫描 src/vdbe.c 源文件生成。位于 ./mkopcodeh.tcl 的脚本执行此扫描并生成 opcodes.h。第二个Tcl脚本 ./mkopcodec.tcl 然后扫描 opcodes.h 生成 opcodes.c 源文件,该文件包含从操作码号到操作码名的反向映射,它用于 EXPLAIN 输出。
keywordhash.h 头文件包含将SQL语言关键字(例如:“CREATE”,“SELECT”,“INDEX”等)映射到 parse.c 解析器使用的数字代码的哈希表定义。keywordhash.h文件由 tool/mkkeywordhash.c 的C语言程序生成。
pragma.h头文件包含了各种定义,用于解析和实现PRAGMA语句。该头文件由脚本tool/mkpragmatab.tcl生成。如果您想添加一个新的PRAGMA,请编辑tool/mkpragmatab.tcl文件以插入解析器用于您新PRAGMA所需的信息,然后运行脚本重新生成pragma.h头文件。
合并
所有单个C源代码和头文件(包括手工编辑和自动生成的)可以合并到一个单一的源文件sqlite3.c中,被称为“合并体”。合并体是推荐在更大应用中使用SQLite的方式。将所有单个源代码文件合并成一个单一的大源代码文件允许C编译器进行更多的跨函数分析并生成更好的代码。与从单个源文件编译相比,从合并体编译SQLite的运行速度大约快5%。
合并体是由工具/mksqlite3c.tcl Tcl脚本生成的。首先,必须收集所有单个源文件到子目录tsrc/中(使用等价的“make target_source”)然后运行tool/mksqlite3c.tcl脚本来重新顺序组合它们,同时解决内部“#include”引用。
合并体的源文件长度超过200K行。一些符号调试器(最著名的是MSVC)无法处理超过64K行的文件。为了解决这个问题,可以运行一个单独的Tcl脚本,叫做tool/split-sqlite3c.tcl,在合并体上运行以将其拆分成一个单一的C文件,称为sqlite3-all.c,它包含了大约七个名为sqlite3-1.c,sqlite3-2.c,...,sqlite3-7.c的其他文件。这样,所有的源代码都包含在一个单一的翻译单元中,以便编译器可以进行额外的跨函数优化,但没有单个源文件超过32K行的长度。
如何融合在一起
SQLite在设计上是模块化的。请参阅架构描述以获取详细信息。其他有用的文档包括文件格式描述、运行预编译语句的虚拟机,关于事务如何工作的描述,以及查询计划器的概述。
SQLite经过多年的努力优化,既追求小型化又追求高性能。优化往往会带来代码的复杂化。因此,当前SQLite实现中存在很多复杂性,不会是世界上最容易黑客攻击的库。
关键文件
-
sqlite.h.in - 此文件定义了SQLite库的公共接口。读者在尝试了解库的内部工作原理之前,需要熟悉此接口。
-
sqliteInt.h - 此头文件定义了许多SQLite内部使用的内部数据对象。除了"sqliteInt.h"之外,一些子系统还有它们自己的头文件。
-
parse.y - 此文件描述了SQLite使用的用于解析SQL语句的LALR(1)语法,以及解析过程中的每一步所采用的操作。
-
vdbe.c - 此文件实现了运行预编译语句的虚拟机。有一些辅助文件,以"vdbe"开头。VDBE可以通过vdbe.h定义的接口访问vdbeInt.h头文件,该头文件定义了内部数据对象。SQLite的其他部分通过vdbe.h定义的接口与VDBE交互。
-
where.c - 此文件(及其以"where*.c"命名的辅助文件)分析WHERE子句并生成虚拟机代码以高效地运行查询。该文件有时被称为"查询优化器"。它有自己的私有头文件whereInt.h,该头文件定义了内部使用的对象。
-
btree.c - 此文件包含SQLite使用的B-树存储引擎的实现。系统其他部分的接口由"btree.h"定义。"btreeInt.h"头文件定义了btree.c内部使用的对象,并不将它们发布到系统其他部分。
-
pager.c - 此文件包含"pager"实现,该模块实现事务。"pager.h"头文件定义了pager.c与系统其他部分之间的接口。
-
os_unix.c 和 os_win.c - 这两个文件通过运行时可插入的VFS接口实现SQLite与底层操作系统的接口。
-
shell.c.in - 此文件不是核心SQLite库的一部分。这是当与sqlite3.a链接时生成"sqlite3.exe"命令行shell的文件。"shell.c.in"文件作为构建过程的一部分转换为"shell.c"。
-
tclsqlite.c - 此文件实现了SQLite的Tcl绑定。这并不是核心SQLite库的一部分。但由于此存储库中的大多数测试都是用Tcl编写的,因此Tcl语言绑定很重要。
-
test.c* - src/目录中以"test"开头的文件用于构建"testfixture.exe"程序。"testfixture.exe"程序是一个增强型Tcl shell。testfixture.exe程序在test/目录中运行脚本以验证核心SQLite代码。当你键入"make test"时,构建和运行testfixture程序(以及一些其他测试程序)。
-
ext/misc/json1.c - 此文件实现了SQLite内建的各种JSON函数。
还有很多其他源文件。每个文件都有一个简洁的头部注释,描述了它在整个系统中的作用和目的。
验证代码的真实性
源树根目录中的manifest
文件包含每个源文件的SHA3-256哈希(针对新文件)或SHA1哈希(针对旧文件)。整个源树的版本名只是manifest
文件的SHA3-256哈希值,如果文件的最后一行以"# Remove this line
"开始,则可能省略该行的最后一行。应该将manifest.uuid
文件包含manifest
文件的SHA3-256哈希值。如果所有上述哈希比较都正确,那么你可以确信你的源树是真实的,没有被篡改。
manifest
文件的格式基本上是自我解释的,但如果您需要详细信息,它们可以在此处找到。
联系方式
主SQLite网站是http://www.sqlite.org/, Geo-distributed backups 在http://www2.sqlite.org/和http://www3.sqlite.org/。