协议演进 / BIP/比特币改进提案 / BIP-93
BIP-93
定义 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 字符集,由三部分组成:
- 人类可读部分:固定为
ms(大写时MS); - 分隔符:固定为
1; - 数据部分:依次为——
- 门槛参数:单个数字
2至9,或0;为0时份额索引必须是s; - 标识符:4 个 Bech32 字符,应为每个主种子(及每套份额)取互不相同的值,以便区分多套秘密;
- 份额索引:任意一个 Bech32 字符,其中
s是特殊值,表示未拆分的秘密本体; - 载荷:最长 69 个 Bech32 字符(长格式例外,见 4.5);
- 校验和:13 个 Bech32 字符。
- 门槛参数:单个数字
全字符串必须全大写或全小写,不得混写;校验计算时统一按小写处理。展示时小写通常更佳,手写宜用大写,编入二维码时应用大写以求紧凑。
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 方案中,原来的秘密字符串本身可以销毁。
初始份额有两种造法:
- 全新秘密:选比特长度(128 至 512 之一)、选门槛 k(2 至 9)、选标识符;随机生成 k 份份额——份额索引按 Bech32 字母表顺序取 a、c、d……,载荷随机取足长度,再补上合法校验和。这 k 份份额定义的秘密事先并不以明文存在,只能由恢复过程反推出。
- 已有秘密:先把现有秘密按 4.3 编码成 codex32 秘密,再按全新秘密的办法随机生成 k−1 份份额;秘密本体加这 k−1 份,构成一组可继续派生的初始份额。
原文另指出:本标准编码的是 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 主种子的原始字节,并额外带门槛、标识符与份额索引的分享结构。把一份备份当成另一种格式去恢复,只会失败或得到毫不相干的结果。
