安全评审
发布之前,BitTool Signer 1.0.0 的源代码在每个平台上都接受了两次 AI 辅助的安全评审,各次评审分别单独进行。四份报告均在下方全文公开。没有一次发现严重或高危问题。iPhone 版的两次评审各发现一项低危问题;Android 版的评审发现了中危问题,其中有四项中危级别的发现在已发布的 Android 1.0.0 中尚未解决,而且其中三项位于两款 App 共用的签名引擎中。
- 这些都不是安全公司进行的审计,也都没有人类审计员参与。
- Opus 5 和 Opus 5.5 评审出自同一模型家族,被评审代码中相当一部分也由它编写或修改。报告指出,不得将其作为第三方证明或认证。
- GPT-6 Astra 的评审说明,它们是为项目所有者编写的 AI 辅助工程评审,而不是审计机构出具的认证,也不是人工签字确认。
- 两份 Android 报告都建议,在用这款 App 保护大额资产之前,先修复尚未解决的中危发现。 BitTool Signer Android 1.0.0 发布时这些问题仍未解决;参见全部发现。
- 独立的第三方审计尚未进行。 报告将它与真机测试、使用真实第三方钱包的往返测试一起,列为存放大额资金之前仍需完成的工作。
BitTool Signer iPhone 版
两份报告
| Opus 5 | GPT-6 Astra | |
|---|---|---|
| 日期 | 2026 年 10 月 19 日 | 2026 年 10 月 18 日(报告 BTS-SEC-2026-1018-R3) |
| 评审的代码 | 标签 v1.0.0,提交 f621372 | 10 月 18 日的工作树:提交 3d706b7,外加 59 个已修改的文件和一个新增测试 |
评审的引擎(signer.js,SHA-256) | 6933d0d8…f21077 | 6933d0d8…f21077 |
| 方法 | 逐行源码评审(3,546 行 JavaScript、11,858 行 Swift)、11 项攻击探测、针对二维码输入的 4,000 例变异测试、测试套件、净室重建 | 按子系统进行源码评审、与上一轮评审对比、独立的 Bitcoin 和 Solana 测试数据、1,509 例变异测试、测试套件、可重现构建、依赖项安全公告 |
| 运行的测试 | 210 项引擎测试、168 项原生测试 | 210 项引擎测试、177 项原生与 UI 测试 |
| 结果 | 严重 0 · 高 0 · 中 0 · 低 1 · 信息 3 | 严重 0 · 高 0 · 中 0 · 低 1 · 观察项 4 |
| 独立性 | 由编写了很大一部分代码的作者进行的自我评审 | AI 辅助,为项目所有者编写 |
两次评审检查的是同一个引擎文件,也就是 BitTool Signer 1.0.0 随附发布的那个文件:所有交易解析器、风险规则和签名计划都在其中。App 的其余部分(界面、钥匙串、存储)则通过阅读 Swift 源码进行评审。
全部发现
| ID | 严重程度 | 发现 | 在 1.0.0 中的状态 |
|---|---|---|---|
| BTS-OP5-01 | 低 | 高风险确认仅在界面中强制执行。对于部分解码和不透明的请求,引擎要求用户明确确认;但对于完全解码的高风险请求(无限额授权、setApprovalForAll、Safe 权限变更、代币销毁),引擎并不要求。App 在勾选确认框之前会禁用“签名”按钮,因此在 1.0.0 中攻击者无法触及这一问题;它之所以重要,是因为引擎是用户可以通过哈希验证的部分。 | 未解决。报告建议在 1.0.1 中将该检查加入引擎。 |
| R3-01 | 低 | 带找零的销毁交易的 Bitcoin 摘要。对于包含金额非零的 OP_RETURN 输出、且有已验证找零的交易,结果页面和活动记录中的简短摘要显示为“Every output returns to your wallet”(所有输出都回到你的钱包)。签名界面本身会正确显示销毁、其金额以及高风险。 | 未解决(1.0.0 随附的引擎就是被评审的版本)。 |
| BTS-OP5-02 | 信息 | 活动记录中的一个本地化键由引擎输出拼接而成,而 SwiftUI 会将其按 Markdown 渲染。它目前可能取到的每个值都是固定标签,因此这是一个潜在问题。 | 未解决,潜在。 |
| BTS-OP5-03 | 信息 | 保险库的密码 KDF(PBKDF2-HMAC-SHA256,600,000 轮)不是内存困难型算法。第二层加密使用与设备绑定的钥匙串密钥,缓解了这一问题:仅凭复制出去的保险库文件,甚至无法发起攻击。 | 已接受;建议在未来的保险库格式中采用 Argon2id。 |
| BTS-OP5-04 | 信息 | Solana 派生过程中的中间 Ed25519 密钥材料没有被显式擦除;真正起作用的控制措施是销毁 JavaScript 上下文。 | 未解决,固有局限。 |
| O-01 | 信息 | 导入时,一次较慢的“该钱包是否已存在?”查询可能在助记词被编辑之后才完成,并为之前的那个钱包提供密码重置。重置操作本身会重新派生钱包身份,并拒绝错误的钱包;保险库不会发生任何变化。 | 报告撰写时尚未解决。 |
| O-02 | 信息 | Safe 和 Solana 的摘要来自范围受限的解码器,而不是来自执行交易。离线状态下无法得知合约的实时状态。 | 设计如此;已在审核界面中披露。 |
| O-03 | 信息 | 在 Swift 和 JavaScript 中无法保证内存被完全清零;物理设备、操作系统随机数生成器以及分发的二进制文件均未经测试。 | 局限。 |
| O-04 | 信息 | 六条仅涉及开发依赖的安全公告(3 条中等级别、3 条低级别)。受影响的包不在随附发布的引擎中。 | 维护事项。 |
两份报告还复测了此前两轮评审的发现,包括一项高级别问题(含 DELEGATECALL 和嵌套 MultiSend 的 Safe 多签交易的摘要),以及 Bitcoin 手续费摘要和 Solana 销毁摘要中的若干低级别问题,确认它们均已修复。Opus 5 还指出,TRON 签名不携带网络标识符,这是 TRON 协议本身的特性而非缺陷;App 已披露这一点。
之后在共用引擎中发现的问题
BitTool Signer iPhone 1.0.0 随附的引擎 SHA-256 为 6933d0d8…f21077。GPT-6 Astra 对 Android 版的评审正是针对这个文件复现了引擎方面的发现,而 Opus 5.5 发现所涉及的 Bitcoin、EVM 和 TRON 代码在该文件中也完全相同。因此,这些引擎方面的发现同样适用于 iPhone 1.0.0:
| ID | 严重程度 | 对 iPhone 1.0.0 的影响 |
|---|---|---|
| BTS-02、BTS-07 | 中 | 与一笔极小转账一起发送的 Solana 销毁、权限变更或授权委托,会被摘要显示为那笔转账。详情中的警告和风险确认仍然保留。 |
| BTS-08 | 中 | 钱包单签配置下任意地址索引的比特币找零都会被接受,包括观察钱包永远不会扫描到的索引。 |
| BTS-09 | 低至中 | 对于未知的 EVM 或 TRON 合约调用,保存的摘要不包含随调用发送的原生币。审核卡片会显示它。 |
上文的 R3-01 与 Android 的 BTS-04 是同一缺陷,Android 版引擎已将其修复。Opus 5.5 报告建议在共用引擎中修复其余问题,修复随后会同时进入两款 App。
评审确认了什么
| 领域 | 已确认 | 详情 |
|---|---|---|
| 签名 | 在 Release 构建中,签名的唯一途径是通过一个固定的计划。六项相互独立的检查将签名与审核过的请求、计划中的钱包以及经批准的密钥绑定在一起,并且引擎在签名前会从解密后的种子重新派生所有内容。已通过实际运行确认。 | 签名前的检查 |
| 随机性 | 助记词来自 SecRandomCopyBytes;出错时创建过程终止,不存在回退。引擎的 crypto.getRandomValues 和 Math.random 被桥接到同一来源。 | 种子生成 |
| 密钥存储 | 两层带认证头部的 AES-GCM、仅限本设备的钥匙串密钥,以及重启后依然有效的失败尝试限速。 | 密钥存储 |
| 拒绝 | 本文档描述的每一种拒绝都成立:SIGHASH_NONE/SINGLE/ANYONECANPAY、缺少前序交易的 SegWit v0 输入、Solana 地址查找表、EIP-7702、blob 交易、原始哈希 eth_sign 以及不支持的链 ID。 | 限制与错误 |
| 不可信输入 | 两次评审共计 5,509 个经过变异的二维码、CBOR、PSBT 和 JSON 输入:无效输入无一被接受,也没有出现未受控的异常。 | UR 与 CBOR |
| 离线 | 既未链接 CFNetwork,也未链接 Network.framework,JavaScript 桥接也不暴露任何网络 API。 | 安全模型 |
| 供应链 | 引擎可以从标签逐字节重建;运行时依赖都锁定了确切版本;生产依赖审计未报告任何漏洞。 | 见下文 |
评审未覆盖的内容
未覆盖的内容包括:物理设备上的测试(故障注入、内存转储、侧信道)、操作系统随机数生成器和 Secure Enclave 的内部机制、引擎文件之外的 App Store 二进制文件来源、真实区块链上的交易、与第三方钱包的往返测试、对密码学依赖的完整审计、21 种界面语言的语言审校,以及形式化验证。测试套件通过,只覆盖它所测试的用例,而不是所有交易或设备。
自行验证引擎
App Store 中的可执行文件无法逐字节重现(Apple 会对其重新签名并加密),但引擎是一个未加密的资源文件,可以进行比对。从标签构建它:
git clone https://github.com/bitbill/bittool-signer-ios
cd bittool-signer-ios && git checkout v1.0.0
cd engine && npm ci --ignore-scripts && npm run build
shasum -a 256 ../BitToolSigner/Resources/signer.js
# 6933d0d8a6f60faa8222506ac0bd7eef80eb884c9f73e31e9f354b4740f21077 (1,181,026 bytes)
然后将它与你所安装 App 中的文件进行比较:使用 Apple Configurator 将 App 下载为 .ipa,解压后计算 Payload/BitToolSigner.app/signer.js 的哈希。完整步骤(包括验证在哪一步止步)见代码仓库中的 Reproducible-Build.md。
BitTool Signer Android 版
两份报告
| Opus 5.5 | GPT-6 Astra | |
|---|---|---|
| 日期 | 2026 年 10 月 25 日 | 2026 年 10 月 24 日 |
| 评审的代码 | 正式版 1.0.0(版本号 1000001):代码提交 71af693,发布记录 62996b8 | 发布之前的提交 e7e71fb:针对上一基线 bb1e656 之后改动的复审 |
评审的引擎(signer.js,SHA-256) | 4b371759…00fa19 | 6933d0d8…f21077,即 iPhone 1.0.0 随附的引擎 |
| 方法 | 分五个领域进行源码评审,每个领域都端到端追踪;每项发现都对照代码复核;用合成数据复现引擎问题;检查依赖项和构建产物 | 评审改动的代码及其调用方,验证三项修复,针对已提交的引擎包复现问题,运行自动化回归检查 |
| 运行的测试 | 2,075 项 App 测试、217 项引擎测试,发布验证脚本 25 项全部通过 | 1,616 项 Android 测试;210 项引擎测试,其中一项失败、一项未实际运行就显示通过(BTS-06) |
| 结果 | 严重 0 · 高 0 · 中 4(其中一项评为低至中)· 低 9 · 若干信息性说明 | 严重 0 · 高 0 · 未解决:中 1、低 1、验证类 1 · 已解决 3 |
| 建议 | 在面向保护大额钱包的正式发布之前,修复 BTS-07、BTS-08 和 BTS-10 | 在该版本下,暂缓面向大额钱包的正式发布 |
| 独立性 | 被评审代码中很大一部分出自同一模型家族 | AI 辅助;未修改生产代码 |
两次评审都覆盖了整个 Android App:Kotlin 模块、原生宿主(基于 QuickJS 的 JNI 层及原生 PBKDF2),以及与 iPhone 版共用的签名引擎。
全部发现
Opus 5.5 评审的就是正式发布的代码,因此以下是 BitTool Signer Android 1.0.0 的发现。
| ID | 严重程度 | 发现 | 所在位置 |
|---|---|---|---|
| BTS-07 | 中 | Solana 权限变更可以隐藏在一笔极小转账之后。一个请求如果在变更账户所有者或权限、授权委托人或包含不透明指令的同时,附带一笔 1 lamport 的转账,那么标题和保存的摘要都只会显示这笔转账。详情中的高风险警告仍然保留,也仍需进行风险确认。 | 共用引擎,iPhone 版同样存在 |
| BTS-08 | 中 | 比特币找零在任意索引上都会被接受。只要某个输出的脚本与钱包任一单签配置相符,无论在分支 0 还是 1、索引最高到 2³¹−1,都会被视为已验证的找零。发往这类地址的资金在审核时显示为“所有输出都回到你的钱包”,但只扫描有限地址范围的钱包可能永远找不到它们。 | 共用引擎,iPhone 版同样存在 |
| BTS-09 | 低至中 | 对于合约调用,保存的摘要不包含原生币金额。一个同时发送原生币的未知 EVM 或 TRON 合约调用,会被保存为“未知合约调用”。审核卡片及其警告会显示这笔金额。 | 共用引擎,iPhone 版同样存在 |
| BTS-10 | 中 | 发布签名密钥存放在开发机上。密钥及其密码以明文保存在工作目录中(已排除在 Git 之外)。 | 发布流程 |
| BTS-11 | 低 | App 锁屏界面后面,二维码相机可能仍在继续扫描。签名仍然需要钱包密码。 | Android App |
| BTS-12 | 低 | 对于完全解码的高风险请求,风险确认只在界面中强制执行。 | Android App(iPhone 版:BTS-OP5-01) |
| BTS-13 | 低 | 在 Android 8–11 上发生 Keystore 错误后,用于验证公开钱包数据的密钥可能被重新生成,钱包随后需要“修复”。不会丢失任何机密或资金。 | Android App |
| BTS-14 | 低 | 两个辅助密钥没有检查屏幕锁定,部分文档也夸大了保护程度。保险库本身仍然要求设置屏幕锁定。 | Android App |
| BTS-15 | 低 | 默认的任务亲和性(task affinity)使旧版 Android 上可能出现任务劫持。 | Android App |
| BTS-16 | 低 | 备份检查可以通过反复试错通过。 | Android App(据报告,iPhone 版相同) |
| BTS-17 | 低 | 12 词助记词的一张恢复卡片只剩约 40 位熵。 | Android App |
| BTS-18 | 低 | 取消钱包匹配后,审核界面可能停留在“正在查找匹配的钱包…”。 | Android App |
| BTS-19 | 低 | Android 8.0–9 无法检测只覆盖确认控件一部分的悬浮层。签名仍然需要密码。 | Android App |
报告还列出了一些信息性说明,例如:引擎线程没有 CPU 时间上限(只影响可用性);在设置单独的上传密钥之前,Google Play 版使用 APK 密钥签名;重试延迟基于系统时钟;以及 JVM 无法擦除的短暂助记词副本。
GPT-6 Astra 评审的是较早的提交。它的发现以及在正式版中的状态:
| ID | 严重程度 | 发现 | 在 e7e71fb 中 | 在 Android 1.0.0 中 |
|---|---|---|---|---|
| BTS-01 | 中 | 部分被遮挡的“签名”和风险确认控件可能透过悬浮层被点按 | 已解决 | 已解决 |
| BTS-02 | 中 | 与极小转账混合的 Solana 销毁或权限变更被摘要显示为那笔转账 | 未解决 | 销毁问题已修复;权限变更问题仍存在,即 BTS-07 |
| BTS-03 | 低 | 操作取消后,机密缓冲区可能残留 | 已解决 | 已解决 |
| BTS-04 | 低 | 带找零的比特币销毁被摘要为“所有输出都回到你的钱包”(即 iPhone 的 R3-01) | 未解决 | 已修复 |
| BTS-05 | 功能 | 中断的二维码扫描无法恢复 | 已解决 | 已解决 |
| BTS-06 | 验证 | 过时的文件路径导致一项引擎测试失败、另一项未实际运行就通过 | 未解决 | 已修复(217 项引擎测试全部通过) |
评审确认了什么
| 领域 | 已确认 |
|---|---|
| 签名 | 没有绕过密码的方法,除了已审核且已匹配的请求之外无法签署任何内容。签名计划与请求哈希、计划哈希和待签字节摘要绑定;引擎会对照解密后的种子重新解析计划,并拒绝对未签名比特币交易的任何改动。取消、App 锁或新请求之后迟到的结果会被丢弃。 |
| 密钥存储 | 原生代码中的 PBKDF2-HMAC-SHA256(600,000 次迭代)、每次加密都使用新随机数和盐的 AES-256-GCM,再加上每个钱包一把不可导出的 Android Keystore 密钥(手机支持时使用 StrongBox)。每次密码尝试都在密钥派生之前计数,计数器损坏时一律拒绝。 |
| 内存中的机密 | 解锁后的机密保存在用完即关闭的容器中,即使操作被取消也会被擦除。助记词通过 App 自带的键盘输入,任何输入法都看不到。 |
| 引擎宿主 | 引擎包按 SHA-256 和长度固定;QuickJS 在没有 eval 和 Function 的情况下运行,每个涉及机密或不可信输入的操作都使用全新的运行时,并设有内存和栈上限。 |
| 离线与平台 | 没有网络权限,也没有联网代码。只导出主界面,没有深层链接;备份和设备迁移被排除在外;所有窗口都不会出现在截图、录屏和最近任务中;在 Android 12 及以上版本会隐藏其他 App 的悬浮层。App 锁永远不能授权签名,发布版不输出任何日志。 |
| 供应链 | 在干净的 npm ci --ignore-scripts 之后重新构建的引擎,与已提交的文件和 APK 中的副本逐字节一致。生产依赖审计没有报告任何安全公告。 |
评审未覆盖的内容
未覆盖的内容包括:真机测试(不同厂商的 Keystore 与 StrongBox 行为、生物识别、悬浮层、屏幕捕获)、恶意悬浮层攻击演练、侧信道、原生代码在 ARM 上的执行、真实区块链上的交易、Maven 和原生依赖的 CVE 扫描,以及 Google Play 构建的副本。两份报告都要求在 Android 8 到最新版本的代表性设备上检验发布版,Opus 5.5 还建议在这款 App 保护大额资金之前进行独立的人工审计。
验证 APK 和引擎
在 bittool.com/signer 发布的 APK 可以完整核验:文件哈希、签名证书、其中的引擎,以及没有网络权限(aapt2 dump badging 列出的权限中没有 android.permission.INTERNET)。
# 1. The APK from bittool.com/signer
shasum -a 256 BitToolSigner-1.0.0-1000001.apk
# 109dbf5c622ee77672904c34a10229df3c9b0eac45b07096c510b1e1e169c2a4
apksigner verify --print-certs BitToolSigner-1.0.0-1000001.apk
# Signer #1 certificate DN: CN=BITTOOL PTE. LTD., O=BITTOOL PTE. LTD., C=SG
# Signer #1 certificate SHA-256 digest: af41ed101765393ae89038eb4c6fc7fa96d930f6fcd76b881437a39b5a9e4631
# 2. The engine inside it
unzip -p BitToolSigner-1.0.0-1000001.apk assets/engine/signer.js | shasum -a 256
# 4b3717599e8ee3ed9d2480e640e7203731543cc8ab0f2c3cec3b78e6f400fa19 (1,181,432 bytes)
# 3. The same engine, rebuilt from source
git clone https://github.com/bitbill/bittool-signer-android
cd bittool-signer-android && git checkout 71af693
cd engine && npm ci --ignore-scripts && npm run build
shasum -a 256 ../core/engine/src/main/assets/engine/signer.js
证书在各个版本之间永不改变,Android 只会安装带有相同证书的更新,App 也会在“设置 › 完全开源 › 本次构建”中显示它。从 Google Play 安装的副本是另一个文件,因为 Google Play 会根据上传的包重新构建,所以无法比对它的哈希;但其中的 assets/engine/signer.js 不会改变,可以在 adb pull 之后用同样的方法比对。完整步骤见代码仓库中的 Reproducible-Build.md。我们已于 2026 年 10 月 25 日对照发布的 APK 核对了上述数值。
下载报告
| 报告 | 页数 | SHA-256 |
|---|---|---|
| iPhone · Opus 5,2026 年 10 月 19 日(PDF,118 KB) | 17 | ff7ddd17e5dffccd99db6aaa43b8f0041167b58bd9b2bd7cc11d185e779bca1a |
| iPhone · GPT-6 Astra,报告 R3,2026 年 10 月 18 日(PDF,46 KB) | 16 | 02d7a358f8f35b2978eab7db1292add4365a94b736ff79e158dee3afb842b64a |
| Android · Opus 5.5,2026 年 10 月 25 日(PDF,252 KB) | 8 | 8242d58aeec1aa88823eb0a938c9461b0681abfab3f37805e2f77a41b3bba34c |
| Android · GPT-6 Astra,复审,2026 年 10 月 24 日(PDF,22 KB) | 7 | b37aaefd40d0d522384a86255accadeb976067175d76e38466a1b4c90b036f53 |
本页的摘要由我们撰写;如果与报告的措辞有出入,以报告为准。