协议演进 / BIP/比特币改进提案 / BIP-93

BIP-93 codex32:带校验、感知 SSSS 的 BIP32 种子

定义 codex32 编码:带 BCH 纠错校验的 base32 字符串,既能直接编码 BIP32 主种子,也能按 Shamir 秘密分享拆成多份分存,且全程可用纸笔手算。本 BIP 目前处于草案状态。

一、档案卡

档案卡
编号BIP-93
标题codex32: Checksummed SSSS-aware BIP32 seeds
作者Leon Olsson Curr and Pearlwort Sneed <[email protected]>, Andrew Poelstra <[email protected]>
指定日期2023-02-13
层级Applications(应用)
类型Informational(信息类)
状态Draft(草案)
阅读版https://bips.dev/93
官方原文https://github.com/bitcoin/bips/blob/master/bip-0093.mediawiki

二、放在哪一族

主种子备份与恢复格式族,与两份既有方案正面对照:

BIP-32:分层确定性钱包——本 BIP 编码的对象就是它的主种子

BIP-39:助记词——同为“给人保管的主种子编码”,是本 BIP 的主要对照组

BIP-173:Bech32——codex32 复用了它的字符集与字符串结构

外部近邻是 SLIP-0039(Shamir 秘密分享助记词):动机与本 BIP 基本相同,但走单词表路线。原文逐条列出与它的区别:codex32 追求简单到可手算、用 Bech32 字母表得到定长紧凑编码、不支持多层秘密分享(拆分享额再拆)、也不支持口令与密钥硬化——后两者正是为手算让路的取舍。

三、它要解决什么

BIP-32 主种子是 HD 钱包一切私钥的源头熵,把它存安全是自托管里最难也最重要的一环。难点是一对天然矛盾:安全性要求备份越少越好,韧性要求备份越多、分布越广越好。给种子加密并不能消除矛盾,只是把问题换成“加密密钥又该怎么备份”。

本 BIP 的出路是 Shamir 秘密分享(SSSS):把秘密拆成 n 份分存,集齐门槛 k 份才能恢复;少于 k 份时,数学上不泄漏秘密的任何信息。于是可以一面把份额广泛分发以求韧性,一面保证个别份额被攻破(只要不足 k 份)不危及秘密本身。

对 BIP-39,原文的批评有三点:它没有纠错能力;不能合理地扩展去支持秘密分享;其校验和太弱。codex32 则把纠错与秘密分享一开始就设计进格式里。

还有一类特殊用户:对电子设备普遍不信任、或秘密需要存活极久的人。本 BIP 要让校验、拆分、恢复全部能用纸笔加查找表完成——每年拿出来手工验一遍份额有没有抄错、锈坏,不必把秘密反复暴露给新硬件。为此必须选用小有限域与线性纠错码;原文强调,只要随机数质量过关,手算能力不以牺牲安全性为代价。当然,手算是可选的,具体手算细节也不在本标准范围内,实现者无须关心。

四、规则本身

4.1 codex32 字符串结构

codex32 形似 Bech32 字符串,复用 BIP-173 的 base32 字符集,由三部分组成:

全字符串必须全大写或全小写,不得混写;校验计算时统一按小写处理。展示时小写通常更佳,手写宜用大写,编入二维码时应用大写以求紧凑。

4.2 校验和与纠错

校验和是一种 BCH 码:对展开长度不超过 93 个符号的码字,保证能检出任意不超过 8 个符号的改动,对更多随机错误漏检的概率小于 2×10⁻²⁰ 量级(原文表述为小于十的二十次方分之三)。

它不只是检错,还能纠错:最多可纠正 4 处字符替换、最多 8 处不可读字符(擦除)、或最多 13 处连续擦除。但实现不得拿纠正后的字符串直接开干:无有效校验和的字符串不得使用;若能给出纠正结果,应提示用户确认(或把纠正结果随错误信息返回、待用户重做一遍动作)。原文还建议实现把形近的非 Bech32 字符当作可纠正对象(如 B→8、O→0、I→l),并把 ? 解释为擦除。

4.3 主种子格式

当字符串是份额索引为 s 的“未拆分秘密”时,载荷就是 BIP-32 主种子的直接编码:把种子比特按最高位优先排好,按 5 比特分组(末尾不足一组时补任意比特),再用 Bech32 字符表逐组翻译。解码时逆序操作,末尾不足 8 比特的残组(至多 4 比特)直接丢弃,且与 BIP-173 不同,不要求被丢弃的比特为零。

支持的主种子长度与总长(常规校验)对应如下:

比特 字节 载荷字符 编码总长
128 16 26 48
160 20 32 54
192 24 39 61
224 28 45 67
256 32 52 74
512 64 103 127(长校验)

门槛参数在未拆分秘密里不参与语义(只需是个数字即可让字符串合法),推荐写 0;4 字符标识符也不起作用,只帮用户区分手头的多份秘密。

4.4 份额:生成与恢复

份额索引不是 s 的字符串就是份额(share),其首字符(门槛参数)必须是 1 以外的非零数字。恢复秘密需要凑齐一组份额,且同时满足:门槛值相同、标识符相同、长度相同、份额索引互不相同、且份额数量恰好等于门槛值。原文给出基于 GF(32) 上拉格朗日插值的 ms32_recover / ms32_interpolate 参考代码——恢复秘密就是对索引 s 对应的点(数值 16)做插值。

反过来,手里有任意 k 份合法份额,就能用同一个插值函数、指定一个新的份额索引,派生出更多新份额;最终的 k-of-n 方案中,原来的秘密字符串本身可以销毁。

初始份额有两种造法:

原文另指出:本标准编码的是 BIP32 主种子的原始字节,而不是像 BIP-39 那样的“熵”——种子与熵在概念上不应混同,这也是 Rationale 一节的主题。

4.5 长 codex32

13 字符的常规校验最多支持展开码长 93,扣掉人类可读部分展开、头部与校验和后,载荷上限 69 字符,够用 32 字节的推荐种子长度,但装不下 BIP32 允许的 64 字节种子。为此定义长格式:改用 15 字符的长校验(另一组 BCH 生成多项式,码长上限 1023,漏检概率更低),载荷可长达 997 字符,展开码长须在 96 至 1023 之间。展开长度 94、95 永远非法。长份额的生成与恢复流程与常规份额完全一致;纠错能力为最多 4 处替换、8 处擦除、或 15 处连续擦除。

原文附有六组正常测试向量、一组无效向量、参考实现,以及一份数学附录(GF[32] 到 GF[1024] 的扩张与两组 BCH 生成多项式的来历)。

五、谁必须动

普通持币人:多了一种备份选项:可以把主种子编码成一份 codex32,或拆成 k-of-n 份额分存多地;需要亲手抄写、定期验校,并记清门槛与标识符。采用与否完全自愿,与现有 BIP39 备份互不排斥,但混用时要分清每份备份的格式。

钱包开发者:实现编解码、BCH 校验与纠错提示、插值恢复与份额派生,并通过原文的全部测试向量(含无效向量);纠错后必须让用户确认,不得静默采用纠正结果。

全节点与矿工、共识层:无须改动。此事发生在钱包离线的备份环节,链上无感知。

六、现在算不算数,以及一个常见误读

状态为 Draft(草案)。按仓库状态含义,这表示文档仍在草案阶段,尚未定稿,更不是已部署的通用做法;原文给出的参数与测试向量可供试验性实现,但读者不应把它当成已经定型的行业标准去托付大额资产,具体采用情况原文未作宣称,本站也不臆测。

常见误读:“codex32 就是带纠错的 BIP39,两者可以互相转换着用。”

不对。两者编码对象与结构都不同:BIP-39 编码的是生成种子之前的熵、再经哈希得到种子;codex32 直接编码 BIP32 主种子的原始字节,并额外带门槛、标识符与份额索引的分享结构。把一份备份当成另一种格式去恢复,只会失败或得到毫不相干的结果。

七、延伸

本页内容整理自 bitcoin/bips 仓库与 bips.dev,事实已与仓库核对;解读仅供学习参考。