作者:tomt1664
来源:https://github.com/commerceblock/mercury/blob/master/doc/blind_musig2.md
本文是一种 Schnorr 多签名协议 Musig2 的一种实现提议,在这种实现中,两方参与签名,但任何一方都无法知晓:(1)完整的共有公钥;以及(2)最终生成的签名。
在下面的描述中,私钥(域元素)以小写字母来表示,而椭圆曲线点用大写字母来表示。G
是椭圆曲线上的生成器点。点乘法使用 X = xG
来表示,点加法使用 A = G + G
来表示。
H()
是一种哈希函数。
Schnorr 签名
在标准的 Schnorr 签名协议中,签名者生成一个私钥(域元素)x
以及对应的公钥 X = xG
。
为了签名一条消息 m
,签名器生成一个临时的 key/nonce(域元素)r
以及对应的公钥点 R = rG
。
签名者计算 e = H(X||R||m)
以及 s = e.x + r
。最终的签名是数据对 (R, s)
。
两方的 Musig2
两方的 Musig2 协议是这样的:
第一个参与者生成私钥 x1
和公钥 X1 = x1G
。第二个参与者生成私钥 x2
和公钥 X2 = x2G
。那么这组公钥可以表示成 L = {X1,X2}
。公钥的聚合系数为 KeyAggCoef(L,X) = H(L,X)
。因此,最终双方共有的(聚合)公钥就是 X = a1X1 + a2X2
,其中 a1 = KeyAggCoef(L,X1)
而 a2 = KeyAggCoef(L,X2)
。
在签名一条消息 m
的时候,第一方生成 nonce r1
和 R1 = r1G
。第二方生成 nonce r2
和 R2 = r2G
。然后聚合成 R = R1 + R2
。
第一方然后计算 c = H(X||R||m)
和 s1 = c.a1.x1 + r1
;第二方计算 c = H(X||R||m)
和 s2 = c.a2.x2 + r2
。
最终的签名就是 (R, s1+s2)
。
盲化的两方 Musig2
在这套协议中,要防止第一方知道完整的公钥和最终的签名是很简单的,只要第一方不需要独立计算和验证 c = H(X||R||m)
即可(因为 TA 在任何时候都无法知晓 m)。
- 密钥聚合仅由第二方来执行。也即只有第一方将自己的公钥
X1
发给对方。 - Nonce 聚合也仅由第二方来执行。第一方将自己的
R1
发送给对方。 - 第二方计算
c = H(X||R||m)
然后发回给第一方,以计算s1 = c.a1.x1 + r1
。
第一方永远不会知道最终的 (R,s1+s2)
和 m
。
密钥更新
为了更新服务端(第一方)的密钥碎片(在用户间转移 statecoin 的时候),每个公钥的密钥聚合系数都必须设为 1。在 Musig2 协议中,使用这个系数是为了防止 “rouge 攻击”,即其中一方可以选择一个从自己的私钥和对方的公钥的倒数中推导出来的公钥,从而可以单方面生成聚合公钥的有效签名。但是,(如 Musig2 论文所述)这也可以通过让各方生成自己所提供的公钥的私钥知识证明来防止。可以直接以一个签名的形式来提供,在 mercury 协议中,签名 statechian 状态时都需要提供签名。
在接收一个 statecoin 时,为了验证钱币的地址(即聚合公钥)在前任所有者和服务端之间正确分享了,客户端必须执行如下验证:
- 检索服务端当前为这个钱币使用的公钥
X1
。 - 检索发送者的公钥(碎片)
X2
。 - 验证
X1 + X2 = P
是这个 statecoin 的地址。 - 验证发送者具有生成
X2
的私钥:这是通过验证X2
对接收者公钥X3
的 statechain 签名来实现的。
这证明了地址 P
是服务端生成的(聚合的),而且只能在服务端合作时才能得到签名,即,没有任何一个前任所有者拥有完整的私钥。
为了更新密钥碎片,需要使用如下协议:
- 服务端(第一方)生成一个随机的盲化 nonce 值
b
并发送给客户端(第二方)。 - 客户端执行
transfer_sender
操作,并将自己的私钥添加到 nonce 中:t1 = b + x2
。 - 客户端发送
t1
给接收者,作为transfer_msg_3
的一部分(使用接收者的公钥X3 = x3G
来加密)。 - 接收者客户端解密
t1
然后减去自己的私钥x3
:t2 = b + x2 - x3
。 - 接收者客户端将
t2
发送给服务端,作为transfer_receiver
的一部分。 - 服务端更新私钥碎片:
x1_2 = x1 + t2 - b = x1 + b + x2 - x3 - b = x1 + x2 -x3
那么现在,x1_2 + x3
(新的服务端私钥碎片和新的客户端私钥碎片的和)就等于 x1 + x2
(旧的服务端私钥碎片和旧的客户端私钥碎片的和)。
- 服务端删除
x1
。
(完)