导读与学习约定
这本手册要解决什么问题
门限签名常被简单描述为“把私钥切成几片,凑够几片就能签名”。这个说法适合建立第一印象,但不够准确:
- 参与者持有的是满足代数关系的密钥份额,而不是把私钥字节切开后的碎片;
- 签名过程通常不会先恢复完整私钥;
- 签名 nonce 也需要门限化,并且其生命周期往往比长期密钥更容易出错;
- 协议安全依赖参与者身份、消息认证、可靠广播、状态机和持久化等额外条件;
- “最终签名可以验证”不代表密钥生成、签名份额或网络流程没有漏洞。
本手册以 Schnorr/FROST 为主线,因为 Schnorr 的线性结构能清晰展示 Shamir 份额如何进入签名方程。门限 ECDSA 被安排在最后,只用于理解复杂度来源,不作为第一个完整实现目标。
适合的读者
你需要具备:
- 基础 Rust:结构体、枚举、trait、模块、
Result、单元测试; - 高中代数水平,并愿意重新学习模运算;
- 能使用 Git 和 Cargo;
- 接受“先证明和测试,再扩展功能”的节奏。
不要求预先掌握椭圆曲线、零知识证明或多方安全计算。
两种实现层次
本工程始终区分两种代码:
toy:透明、可手算、故意不安全
用于理解有限域、多项式和攻击。可以使用很小的素数、确定性随机数和可打印的秘密。它的价值是教学透明度,绝不能承载真实秘密。
protocol:接近规范、仍非生产级
使用 Ristretto255、SHA-512、系统随机数、规范编码和严格状态机。它应当对照 RFC 9591 测试向量和成熟实现,但仍然只是学习工程。
不要让 toy 类型悄悄进入 protocol;最好使用独立模块和明显的类型名称。
威胁模型从简单到复杂
第一阶段的假设:
- 所有参与者运行在同一个本地进程中;
- 参与者名单固定;
- 有一个可信 Dealer 生成并分发份额;
- coordinator 可以看到公开消息并阻断协议,但不能伪造已认证参与者;
- 暂不处理网络丢包、进程崩溃、磁盘泄露和侧信道。
后续逐项移除假设:
- 用 VSS 检测 Dealer 发放错误份额;
- 用 DKG 移除知道完整私钥的 Dealer;
- 用显式状态机处理重复、乱序和跨会话消息;
- 用持久化的 nonce 消耗记录处理崩溃恢复;
- 用认证信道和可靠广播明确网络假设。
每次扩展前,都要写下“新增能力”和“仍未解决的问题”。
本手册的符号约定
| 符号 | 含义 |
|---|---|
| \(q\) | 素数阶群的阶,也是标量域模数 |
| \(\mathbb{F}_q\) | 模 \(q\) 的有限域 |
| \(G\) | 群生成元 |
| \(x\) | 私钥标量 |
| \(X=xG\) | 群公钥 |
| \(i\) | 非零、唯一的参与者标识符 |
| \(s_i\) | 参与者 \(i\) 的秘密份额 |
| \(Y_i=s_iG\) | 参与者 \(i\) 的公开验证份额 |
| \(t\) | 恢复或签名所需的最少参与者数 |
| \(n\) | 总参与者数,满足 \(1\le t\le n\) |
| \(H\) | 带域分离、输出到标量域的哈希函数 |
标量使用小写字母,群元素使用大写字母。所有除法都表示乘以有限域中的逆元,不是普通浮点除法。
三条贯穿全书的规则
- 秘密 nonce 只能使用一次。 “同一消息可以复用”也是错误的。
- 密码 transcript 必须规范编码。 不要哈希
Debug、JSON 文本或未规定字段顺序的序列化结果。 - 先验证再聚合。 参与者 ID、点、标量、承诺和签名份额都可能来自敌手。
如何使用检查题
不要只在脑中回答。把答案写下来,最好附一段公式、一个反例或一个测试名称。如果不能解释某个结论,先回到推导,而不是继续堆代码。