环签名
如何证明“我拥有这些密钥中的一把”,却不说出是哪一把
环签名证明签名者知道环中 n 个公钥里某一个的私钥,但不透露是哪一个。门罗币使用的是可关联的版本——签名附带一个 key image,使同一个输出无法被花费两次。目前的方案是 CLSAG,环大小为 16。
先看普通的 Schnorr 签名
要理解环签名,先看看如何证明知道 P = x·G 中的一把私钥 x:
- 签名者选取一个随机数
α,并计算α·G。 - 计算挑战值
c = Hs(message ‖ α·G)。 - 计算响应值
s = α − c·x。签名为(c, s)。 - 验证者重建
s·G + c·P = (α − cx)·G + c·x·G = α·G,并检查Hs(message ‖ α·G) = c。
不知道 x,就无法为给定的 c 找到 s:c 取决于 α·G,而它必须在你得知 c 之前就固定下来。
环的思路:让挑战链首尾相接
现在我们有 n 个公钥 P₀ … Pₙ₋₁,只知道其中一个的私钥,比如 Pπ。技巧在于:每个挑战值都由前一环节计算得出,整条链首尾相接形成一个环。
- 在真实密钥的位置 π,取一个随机数
α,计算下一个挑战值:c_{π+1} = Hs(m ‖ α·G)。 - 对之后的每个(他人的)公钥,选取一个随机的
sᵢ,计算c_{i+1} = Hs(m ‖ sᵢ·G + cᵢ·Pᵢ)。沿着环走一圈。 - 回到 π,此时已经知道
c_π。要让环闭合,需要s_π·G + c_π·P_π = α·G,即s_π = α − c_π·x。这只有知道 x 才能计算出来。 - 签名为:
(c₀, s₀, s₁, …, sₙ₋₁)。
验证者从 c₀ 出发,用同样的公式走完每一个环节,并检查最后是否回到了同一个 c₀。所有环节看起来都一样——从签名中无法看出“闭合”发生在哪里。
可关联性:LSAG
普通的环签名无法阻止同一个输出在不同的环中被花费两次。因此门罗币使用可关联签名:签名者公布一个 key image I = x·Hp(P_π),并在每个环节中额外证明同一个 x 把 I 与 Hp(Pᵢ) 联系在一起:
同一把密钥使用两次会得到相同的 I——网络会拒绝第二次花费。
MLSAG → CLSAG
在 RingCT 中,每个环成员需要证明的不是一个而是两个陈述:知道输出的私钥,以及承诺之差是对零的承诺(见 RingCT)。最初(2017 年)这是用 MLSAG 完成的——本质上是把两个环粘在一起:每个环成员有两个 s 值。
2020 年 10 月(v13 硬分叉),它被 CLSAG(Concise LSAG,简洁 LSAG)取代:同一位置上的两把密钥通过以哈希为系数的随机线性组合合并为一把。每个环成员只需一个 s。签名缩小了约 25%,验证也更快。
环大小的历史
| 时间 | 最少成员数 | 说明 |
|---|---|---|
| 2014 | 1(无诱饵) | CryptoNote 遗留问题:允许“0-mixin”交易 |
| 2016 | 3 | 硬分叉,禁止无诱饵的环 |
| 2017 | 5 | 与 RingCT 一同引入 |
| 2018 年 4 月 | 7 | 在关于连锁反应的研究之后 |
| 2018 年 10 月 | 11,固定 | 所有人的环大小统一 |
| 2022 年 8 月 | 16,固定 | v15 硬分叉(与 Bulletproofs+ 和 view tag 一起) |
为什么要固定?如果每个人都自己选择,非标准的环大小就会成为识别钱包的指纹。
为什么不用 1000?
CLSAG 的大小和验证时间随环大小线性增长。1000 个成员的环意味着每个输入约 32 KB,节点同步速度会慢几十倍。人们研究过大小呈对数增长的方案(Triptych、Arcturus、Seraphis),但最终门罗币选择了最彻底的方案——FCMP++,其中的“环”就是整条区块链。