跳到主要内容

签名器在签名前会检查什么

来自在线设备的二维码属于不可信输入。BitTool Signer 将每个请求都视为潜在的恶意请求,并对每条链运行相同的处理流程:

解码与审核无需密码
授权与签名每个任务都需输入密码
  1. 1组装二维码帧,并严格解码请求(CBOR、PSBT、交易字节)。
  2. 2解析将要签名的确切字节,并生成审核内容:金额、收款方、手续费、合约调用、警告。
  3. 3仅使用公钥将请求匹配到本地钱包,并确定一个与请求哈希绑定的签名计划。
  4. 用户审核、确认风险、输入密码 ▶
  5. 4从种子重新派生密钥,并在使用前与计划进行核对。
  6. 5签名,验证新签名,并按请求自身的格式编码响应。

严格解码​

在运行任何链相关逻辑之前,先验证传输层:

  • CBOR 必须使用定长编码,不能有重复的 map 键,不能有尾随字节,最多 24 层嵌套、20,000 个数据项。未知的请求字段会被拒绝。
  • 动态二维码必须来自同一个会话。混合两个请求的帧会被视为错误,而不会被合并。
  • 大小限制:2,048 帧,每帧 4,300 个字符,1 MiB 扫描文本,128 KiB 解码后载荷。

原始的 CBOR(或 PSBT)字节会附加在解码后的请求上。就在签名之前,引擎会重新解码这些字节;如果它们与审核时的内容不再一致,就拒绝签名。

审核展示的是真实字节​

引擎自己解析交易或消息;从不信任在线钱包提供的摘要。例如:

  • Bitcoin:每个输入和输出及其金额和地址、手续费,以及哪个输出是找零。只有当签名器能用自己的公钥重新推导出某个输出的脚本时,该输出才算作找零。仅凭 PSBT 标签,绝不会让一个输出成为“找零”。
  • EVM:网络、nonce、手续费、收款方、原生币金额,以及解码后的调用(ERC-20/721/1155 转账和授权、Permit、Permit2、Safe 交易等)。spender 和 operator 地址会与内置的已知公开合约列表进行比对;未知的 spender 会提高风险等级。
  • TRON:合约类型、金额、TRC-20/721 调用、手续费上限、权限 ID。
  • Solana:它认识的每条指令(System、SPL Token、Token-2022、ATA、Compute Budget、Memo、Stake 和 NFT 程序)、手续费支付方和签名者。

每个请求都会得到两个相互独立的标签:

解码程度风险
完全解码(Fully decoded)· 部分解码(Partially decoded)· 不透明(Opaque)正常(Normal)· 警告(Warning)· 高(High)

对于有效但未完全解码的请求(例如未知的合约调用),仍然可以签名,但用户必须针对该请求明确确认风险。不存在一直保持开启的“盲签”设置。格式错误的字节、不支持的交易类型以及相互冲突的网络数据会被直接拒绝,且无法绕过。

1.0.0 中的已知问题

除特别说明外,以下问题都位于两款 App 共用的引擎中。参见安全评审。

  • 风险确认(iPhone 上为 BTS-OP5-01,Android 上为 BTS-12,低)。完全解码的高风险请求,例如无限额授权或 setApprovalForAll,同样需要明确确认,但这条规则由 App 的界面强制执行(“签名”按钮保持禁用);引擎只对部分解码和不透明的请求强制执行。无法通过二维码绕过。
  • Solana 摘要(BTS-07,中)。与一笔极小转账一起发送的权限变更、授权委托或不透明指令,会被摘要显示为那笔转账。在 iPhone 1.0.0 上,销毁也可能以同样方式被掩盖。警告和风险确认仍然保留。
  • 比特币找零(BTS-08,中)。钱包自身单签配置下任意索引上的输出都会被视为找零,包括观察钱包永远不会扫描到的索引。
  • EVM 和 TRON 摘要(BTS-09,低至中)。对于未知合约调用,保存的摘要不包含随调用发送的原生币金额;审核卡片会显示它。

匹配正确的钱包​

用户从不选择由哪个钱包签名。引擎会将请求与每个本地钱包的公开数据进行比较,并且只接受恰好一个匹配:

链使用的依据
Bitcoinbip32Derivation 中的指纹和路径,并与输入的实际脚本进行核对。没有派生数据时,只尝试每个单签账户的 /0/0。
EVM派生路径(+ 主指纹)和/或地址。没有路径时只比较索引 0。
TRON派生路径(任意索引),或与索引 0–99 进行比较的地址。
Solana三个预设密钥之一,并且它还必须占据消息中的某个必需签名者槽位。

无匹配、缺少签名者、匹配不唯一(两个钱包都符合)和钱包数据不完整,分别是不同的错误。签名器从不逐个尝试每个钱包的种子。

匹配结果是一个不可变的计划,它绑定了规范请求哈希、原始字节、确切的待签名字节,以及目标密钥和输入。输入密码后,引擎会重新派生密钥、重建公开数据、重新运行匹配,并在接触私钥之前比对完整的计划。

这对你的集成意味着什么

始终包含派生数据:PSBT 中的 bip32Derivation,以及 eth-、tron- 和 sol-sign-request 中的路径和指纹。签名者数据完整的请求会立即匹配;缺少这些数据的请求会回退到索引 0,对于其他任何账户都可能失败。

将签名绑定到请求​

  • 引擎对整个规范请求做哈希、签名、验证自己的签名,然后将结果连同原始请求哈希一起返回。如果在签名过程中用户离开了 App,或有新请求替换了旧请求,迟到的结果会被丢弃。
  • EVM、TRON 和 Solana 的响应会回传请求 ID,签名覆盖的正是审核过的确切字节。Bitcoin 响应返回添加了签名的同一个 PSBT;其他所有字段都必须保持不变。
  • 一个 PSBT 可能需要多个本地钱包签名(例如同一部手机上持有两个密钥的多签)。每个钱包是一个独立的任务,需输入各自的密码,部分签名会在任务之间保留。

1.0.0 版本的两次安全评审都检查了这条链路。Opus 5 评审通过实际执行确认:被篡改的计划会被拒绝,并且 Release 构建中唯一的签名入口会经过上述所有检查。

离线无法覆盖的范围​

当审核无法得知某些信息时,会明确说明:代币合约的行为、当前余额和授权额度、某个 TRON 多签成员是否仍被授权、某个 Solana blockhash 是否仍然有效,或者某个 Blur Merkle 根实际承诺的内容。你的钱包处于在线状态,更适合在生成二维码之前展示这些背景信息。