本文深入探讨了Schnorr签名及其在比特币中的应用,包括Schnorr签名的基本原理、线性特性、适配器签名、Musig2协议以及Musig2与BIP 32推导的结合使用。文章详细解释了这些技术概念,并提供了相应的算法步骤和示例,展示了Schnorr签名在构建更高级加密方案中的潜力。
比特币在 Taproot 更新中增加了对 Schnorr 签名的支持,该更新在区块 709 632 处激活。 在本文中,我们将深入探讨它的工作原理以及它解锁的一些高级方案。
Schnorr 签名在 BIP 340 中指定。 忽略 BIP 中描述的许多细节,从高层次上讲,签名算法的工作原理如下:
P = k*G -> 签名者的公钥
m -> 要签名的消息
签名:
r = {0;1}^256 -> 随机 nonce
R = r*G
e = H(R || P || m)
s = r + e*k
(s, R) -> 消息签名
验证:
e = H(R || P || m)
R' = s*G - e*P
如果 R = R',则签名有效
与 ECDSA 签名相比,schnorr 签名的一个非常有趣的特性是,如果我们稍微改变签名算法,它们可以很容易地组合给定消息的签名。
A = a*G -> Alice 的公钥
B = b*G -> Bob 的公钥
P = A + B -> 组合公钥
m -> 要签名的消息
签名:
ra = {0;1}^256 -> Alice 生成的随机 nonce
RA = ra*G -> Alice 发送给 Bob 的公共部分 nonce
rb = {0;1}^256 -> Bob 生成的随机 nonce
RB = rb*G -> Bob 发送给 Alice 的公共部分 nonce
R = RA + RB -> 公共 nonce
e = H(R || P || m)
sA = ra + e*a -> Alice 生成的部分签名
sB = rb + e*b -> Bob 生成的部分签名
s = sA + sB
(s, R) -> 组合消息签名
验证:
e = H(R || P || m)
R' = s*G - e*P
如果 R = R',则签名有效
你应该注意到验证算法没有改变。 这意味着验证者甚至不知道有多个签名者参与:签名看起来像是来自一个标准的单个签名者。
:warning: 当 Alice 或 Bob 是恶意的时候,这个签名方案是不安全的。我们将在下面的 Musig2 章节中描述一个安全的签名方案。
schnorr 签名的线性性也允许通过签名揭示一个秘密。
P = k*G -> 签名者的公钥
T = t*G -> tweak (t 是将被揭示的秘密)
m -> 要签名的消息
签名:
r = {0;1}^256
R = r*G
e = H(R + T || P || m) -> 注意我们在这里用 T 调整 nonce
s = r + e*k -> 但我们没有在这里用 t 调整它
(s, R, T) -> 适配器签名:当生成 nonce R + T 的有效签名时,将自动揭示 t
验证:
e = H(R + T || P || m)
R' = s*G - e*P
如果 R = R',则适配器签名有效
请注意,(s, R) 或 (s, R + T) 不是有效的 schnorr 签名
完成:
s' = s + t
R' = R + T
(s', R') -> 有效的 schnorr 签名
提取:
e' = H(R' || P || m)
R'' = s'*G - e'*P
如果 R'' = R',则签名有效
t = s' - s
验证者通过 schnorr 签名学习了 t
注意:为了使这个工作,验证者必须确保 nonce R + T
预先被固定。
否则,签名者可能会为不相关的 nonce 创建一个有效的签名,这不会揭示秘密 t
。
当与 Musig2 结合使用时,这是最有用的,参与者在签名之前提交 nonce。
适配器签名也可以以相反的方式使用。
如果签名者不知道秘密 t
,它可以生成一个适配器签名。
然后,另一个知道 t
的参与者可以将该适配器签名转换为有效签名。
Musig2 是一种安全的方案,用于将多个签名组合成单个 schnorr 签名。 它只需要参与者之间进行两轮通信,并且第一轮可以提前完成(独立于要签名的消息)。 该方案中的新颖之处在于为每个参与者使用多个 nonce,并以一种巧妙的方式组合它们以生成最终的 nonce。 这产生了一个优雅的方案,看起来与标准 schnorr 签名非常相似。
PA = pa*G -> 参与者 A 的公钥
PB = pb*G -> 参与者 B 的公钥
L = (PA, PB) -> 参与者公钥的排序列表
P = H(H(L) || PA)*PA + PB -> 组合公钥
NonceGenA (由参与者 A 运行):
ra1 = {0;1}^256
ra2 = {0;1}^256
RA1 = ra1*G
RA2 = ra2*G
NonceGenB (由参与者 B 运行):
rb1 = {0;1}^256
rb2 = {0;1}^256
RB1 = rb1*G
RB2 = rb2*G
NonceExchange (通信第一轮):
参与者 A 将 RA1, RA2 发送给参与者 B
参与者 B 将 RB1, RB2 发送给参与者 A
SignA (由参与者 A 运行):
x = H(P || RA1 + RB1 || RA2 + RB2 || m)
R = RA1 + RB1 + x*(RA2 + RB2)
ra = ra1 + x*ra2
e = H(R || P || m)
sa = ra + e*H(L || PA)*pa
(sa, R) -> 参与者 A 的部分签名
SignB (由参与者 B 运行):
x = H(P || RA1 + RB1 || RA2 + RB2 || m)
R = RA1 + RB1 + x*(RA2 + RB2)
rb = rb1 + x*rb2
e = H(R || P || m)
sb = rb + e*pb
(sb, R) -> 参与者 B 的部分签名
Combine (通信第二轮):
s = sa + sb
(s, R) -> 公钥 P 的有效 schnorr 签名
验证:
e = H(R || P || m)
R' = s*G - e*P
= (sa + sb)*G - e*P
= (ra + e*H(L || PA)*pa)*G + (rb + e*pb)*G - e*P
= (ra + rb)*G + e*(H(L || PA)*pa + pb)*G - e*P
= R + e*P - e*P
= R
-> 这是一个有效的 schnorr 签名
注意:我们只使用了两个参与者来简化示例,但 Musig2 适用于任意数量的参与者。
Musig2 可以与适配器签名结合使用:
## 第一轮(预计算 nonce)是普通的 Musig2
PA = pa*G -> 参与者 A 的公钥
PB = pb*G -> 参与者 B 的公钥
L = (PA, PB) -> 参与者公钥的排序列表
P = H(H(L) || PA)*PA + PB -> 组合公钥
NonceGenA (由参与者 A 运行):
ra1 = {0;1}^256
ra2 = {0;1}^256
RA1 = ra1*G
RA2 = ra2*G
NonceGenB (由参与者 B 运行):
rb1 = {0;1}^256
rb2 = {0;1}^256
RB1 = rb1*G
RB2 = rb2*G
NonceExchange (通信第一轮):
参与者 A 将 RA1, RA2 发送给参与者 B
参与者 B 将 RB1, RB2 发送给参与者 A
## 第二轮简单地用秘密调整组合 nonce
T = t*G -> 只有 B 知道的秘密 t
SignA (由参与者 A 运行):
x = H(P || RA1 + RB1 + T || RA2 + RB2 || m)
R = RA1 + RB1 + x*(RA2 + RB2)
ra = ra1 + x*ra2
e = H(R + T || P || m)
sa = ra + e*H(L || PA)*pa
(sa, R + T) -> 参与者 A 的部分签名
SignB (由参与者 B 运行):
x = H(P || RA1 + RB1 + T || RA2 + RB2 || m)
R = RA1 + RB1 + x*(RA2 + RB2)
rb = rb1 + x*rb2
e = H(R + T || P || m)
sb = rb + e*pb
(sb, R, T) -> 参与者 B 的适配器签名
参与者 A 可以在发送其部分签名之前验证参与者 B 的适配器签名:
(sa + sb)*G 必须等于 R + H(R + T || P || m)*P
-> 注意:这不是一个有效的 schnorr 签名,请注意 R(在 **hash** 之外)和 R + T(在 **hash** 内部)之间的不匹配
Complete (由参与者 B 运行):
s = sa + sb + t
R' = R + T
(s, R') -> 公钥 P 和 **nonce** R + T 的有效 schnorr 签名
Extract (由参与者 A 运行):
t = s - sa - sb
Musig2 可以与 BIP 32 推导一起使用。 诀窍是个别密钥不会改变,我们只是在调整聚合公钥。
PA = pa*G -> 参与者 A 的公钥
PB = pb*G -> 参与者 B 的公钥
L = (PA, PB) -> 参与者公钥的排序列表
P = H(H(L) || PA)*PA + PB -> 组合主公钥
两个参与者都同意一个链码 c
可以计算索引 i 处的组合公钥:
I = H(c, P || i)
IL = I[0:32]
IR = I[33:64]
Pi = P + IL*G
ci = IR
参与者使用以下步骤为此子组合公钥创建部分签名。
这些步骤与普通的 Musig2 相同,只是用 Pi 代替 P。
NonceGenA (由参与者 A 运行):
ra1 = {0;1}^256
ra2 = {0;1}^256
RA1 = ra1*G
RA2 = ra2*G
NonceGenB (由参与者 B 运行):
rb1 = {0;1}^256
rb2 = {0;1}^256
RB1 = rb1*G
RB2 = rb2*G
NonceExchange (通信第一轮):
参与者 A 将 RA1, RA2 发送给参与者 B
参与者 B 将 RB1, RB2 发送给参与者 A
SignA (由参与者 A 运行):
x = H(Pi || RA1 + RB1 || RA2 + RB2 || m)
R = RA1 + RB1 + x*(RA2 + RB2)
ra = ra1 + x*ra2
e = H(R || Pi || m)
sa = ra + e*H(L || PA)*pa
(sa, R) -> 参与者 A 的部分签名
SignB (由参与者 B 运行):
x = H(Pi || RA1 + RB1 || RA2 + RB2 || m)
R = RA1 + RB1 + x*(RA2 + RB2)
rb = rb1 + x*rb2
e = H(R || Pi || m)
sb = rb + e*pb
(sb, R) -> 参与者 B 的部分签名
Combine (通信第二轮):
e = H(R || Pi || m) -> 任何能够计算这个的人都可以完成这一步
s = sa + sb + e*IL
(s, R) -> 公钥 Pi 的有效 schnorr 签名
验证:
e = H(R || Pi || m)
R' = s*G - e*Pi
= (sa + sb + e*IL)*G - e*Pi
= (ra + e*H(L || PA)*pa)*G + (rb + e*pb)*G + e*IL*G - e*Pi
= (ra + rb)*G + e*(H(L || PA)*pa + pb + IL)*G - e*Pi
= R + e*Pi - e*Pi
= R
-> 这是一个有效的 schnorr 签名
- 原文链接: github.com/t-bast/lightn...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!