比特币 - Schnorr魔法 - T-Bast

  • t-bast
  • 发布于 2022-03-02 21:52
  • 阅读 15

本文深入探讨了Schnorr签名及其在比特币中的应用,包括Schnorr签名的基本原理、线性特性、适配器签名、Musig2协议以及Musig2与BIP 32推导的结合使用。文章详细解释了这些技术概念,并提供了相应的算法步骤和示例,展示了Schnorr签名在构建更高级加密方案中的潜力。

Schnorr 的魔法

比特币在 Taproot 更新中增加了对 Schnorr 签名的支持,该更新在区块 709 632 处激活。 在本文中,我们将深入探讨它的工作原理以及它解锁的一些高级方案。

目录

Schnorr 签名

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',则签名有效

Schnorr 签名的线性性

与 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

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 适配器签名

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 推导

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 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
t-bast
t-bast
江湖只有他的大名,没有他的介绍。