阈值方案攻击:第一部分

  • hexens
  • 发布于 1天前
  • 阅读 16

本文深入探讨了阈值密码学在实际应用中面临的挑战,通过分析Pedersen DKG、MtA、BitGo钱包等多个真实案例,揭示了诸如多项式阶数验证缺失、离散对数检查疏忽、知识证明遗漏、哈希编码歧义等漏洞攻击手段,强调了从理论安全到生产安全过渡过程中严格安全审计及参数验证的重要性。

阈值密码学在纸面上看起来无懈可击。你将信任分散到多个参与者之间,需要一个最小阈值来重构秘密或生成签名,突然间你就获得了针对单点故障的弹性。数学上是成立的,安全证明也很优雅,而且协议让人感觉坚如磐石。

然后你将其部署到生产环境,事情就开始出错了。有时是一个缺失的验证检查,没人觉得它重要。有时是数据编码方式上的一个微妙歧义。"可证明安全的阈值方案"和"不会让用户破产的生产就绪实现"之间的差距比大多数人意识到的要大。

这篇文章将介绍对真实阈值系统的真实攻击,这是安全审计人员在实际环境中发现的那种。目的不是让你害怕阈值方案,它们仍然是我们拥有的用于分布式信任的最佳工具之一。目的是给你一个实际的检查清单,列出实际出错的地方,以便你可以在自己的实现上线主网之前,用清晰的眼光对其进行审计。

Pedersen DKG: 错误阶数多项式攻击

在 Frost 的审计过程中,Trail of Bits 发现了一个有趣的漏洞。参与者不检查多项式系数的长度,这意味着多项式的阶数可能高得多,这将导致无法恢复密钥。

Pedersen 的分布式密钥生成建立在 Feldman 的可验证秘密共享之上,后者通过让参与者在没有可信经销商的情况下验证他们的份额来扩展 Shamir 的方案。基本流程:每个参与方 $P_i$ 生成一个随机的 $t$ 阶多项式 $pi(x) = a{i,0} + a{i,1}x + \cdots + a{i,t}x^t$,其中 $a_{i,0}$ 是他们的秘密贡献。他们发布对每个系数的承诺:

$A{i,0} = g^{a{i,0}}, \quad A{i,1} = g^{a{i,1}}, \quad \ldots, \quad A{i,t} = g^{a{i,t}}$

然后 $Pi$ 将计算结果 $s{i,j} = p_i(j)$ 私下发送给每个参与方 $P_j$。接收方 $P_j$ 通过检查来验证他们的份额:

$g^{s{i,j}} \stackrel{?}{=} \prod{k=0}^{t} A_{i,k}^{j^k}$

为了形成最终的共享秘密,每个参与方 $P_j$ 将他们收到的所有份额相加:$sj = \sum{i=1}^{n} s{i,j}$。集体多项式为 $p(x) = \sum{i=1}^{n} pi(x)$,其阶数为 $t$,秘密为 $s = p(0) = \sum{i=1}^{n} a_{i,0}$。任何 $t+1$ 个参与方都可以通过拉格朗日插值法恢复 $s$。

攻击

在对 FROST 实现的审计过程中,他们发现许多实现没有验证承诺向量的长度。一个恶意的 $P_i$ 可以提交一个阶数为 $T > t$ 的多项式,例如,阶数为 $2t$ 甚至阶数为 $n$。由于最终多项式的阶数是所有输入阶数中的最大值,这会在无声无息中将重构阈值从 $t+1$ 提高到 $T+1$。

如果 $T \geq n$,则秘密变为不可恢复:参与方的任何子集都无法重构它,即使在每个人的合作下也是如此。所有份额现在都被锁定,并且由该密钥控制的任何资金都将永久丢失。即使 $T < n$,仅有 $t+1$ 个参与方的签名尝试也会神秘地失败,并且实现可能会将失败归咎于诚实的参与者。

修复方法很简单:检查每个承诺向量是否正好有 $t+1$ 个元素。Trail of Bits 发现了十个易受攻击的实现,包括多个 FROST 变体和 GG18/GG20 库。

MtA 中缺少离散对数检查

乘法到加法 (MtA) 协议在阈值方案中很常见,它将乘积 $a \times b$ 的份额转换为加法份额 $\alpha + \beta \equiv a \times b \pmod{q}$。大多数实现都包含零知识证明以确保参与方诚实地行事,但这些证明需要仔细设置。

设置

每个参与方都为 ZK 证明生成辅助参数:

  • 一个 RSA 模数 $\tilde{N} = pq$(因式分解保密)
  • 两个群元素 $h_1, h2 \in \mathbb{Z}{\tilde{N}}^*$,它们应该是无关的(它们之间没有已知的离散对数)

证明中的承诺通常看起来像 $z = h_1^m h2^{\rho} \mod \tilde{N}$,其中 $m$ 是要证明的值,$\rho$ 是盲化随机数。安全性取决于证明者不知道 $\log{h_1}(h_2)$,如果他们知道,他们就可以打破证明的可靠性。

攻击

该协议应该包括一项检查,以确保 $h_1$ 和 $h_2$ 生成相同的循环子群,并且它们之间的离散对数关系是未知的。但是许多实现完全跳过了此验证,信任发送者已正确生成它们。

攻击者通过设置 $h_2 = 1$ 来利用这一点。现在承诺崩溃了:

$z = h_1^m h_2^{\rho} = h_1^m \cdot 1^{\rho} = h_1^m \mod \tilde{N}$

盲化项消失了,$z$ 直接揭示了 $h_1^m$。为了提取 $m$,攻击者有两种选择:

选项 1:整数对数 选择非常大的 $\tilde{N}$,以便在 $\mathbb{Z}_{\tilde{N}}^*$ 中计算离散对数变得与计算整数对数一样困难,而使用标准算法计算整数对数非常简单。

选项 2:Pohlig-Hellman 选择 $\tilde{N}$ 作为许多小素数的乘积:$\tilde{N} = p_1 p_2 \cdots p_k$,其中每个 $pi$ 都很小。那么 $\mathbb{Z}{\tilde{N}}^*$ 的阶数为 $\phi(\tilde{N}) = (p_1-1)(p_2-1)\cdots(p_k-1)$,它是光滑的。使用 Pohlig-Hellman,攻击者计算每个因子 $m \bmod (p_i-1)$(速度很快,因为因子很小),然后通过中国剩余定理重构 $m$。

无论哪种方式,攻击者都会恢复 $m$ 并打破证明。这会泄露受害者本应受到零知识属性保护的秘密值。

漏洞已在 Binance 的 TSS 库中发现,并在论文 "攻击阈值钱包" 中进行了描述。

修复: 发送者应以零知识证明 $\log_{h_1}(h_2)$ 是未知的,并且 $h_1$ 和 $h_2$ 生成相同的循环群。此外,必须证明 Paillier 模数 $\tilde{N}$ 是两个大小足够的素数的乘积。

BitGo 钱包:缺少知识证明

知识证明不仅仅是形式,它们是防止恶意方将垃圾数据输入协议的关键检查点。BitGo 钱包漏洞显示了跳过它们会发生什么。

协议

BitGo 的 TSS 实现遵循 GG18 密钥生成协议,该协议分为三个阶段:

  1. 承诺:每个参与者 $P_i$ 选择一个秘密密钥份额 $u_i$ 并广播:

    • 对其秘密的承诺 $g^{u_i}$
    • 模数为 $N_i$ 的 Paillier 公钥 $E_i$
  2. 揭示和共享:每个参与者打开他们的承诺(揭示 $u_i$),然后执行 Feldman VSS 以分发 $u_i$ 的份额。最终的共享秘密为 $x = \sum u_i$,公钥为 $y = g^x = \prod g^{u_i}$。每个参与者最终都获得一个份额 $x_i$,以便任何阈值子集都可以重构 $x$。

  3. 证明:每个参与者都应该以零知识证明:

    • 了解他们的份额 $x_i$(知识证明)
    • 他们的 Paillier 模数 $N_i = p_i q_i$ 正好是两个素数的乘积(双素性证明)

BitGo 的实现完全跳过了第 3 阶段。没有证明。只有信任。

攻击

攻击者通过选择恶意的 Paillier 模数 $N = pq$ 来利用缺少双素性证明的漏洞,其中 $q = 2p + 1$。这是一种安全的素数构造,但有一个问题:乘法群 $\mathbb{Z}_N^*$ 具有泄露信息的特殊结构。攻击者还选择任何平方数 $V \in \mathbb{Z}_N^*$(例如,$V = 4$)并广播 $(N, V)$ 作为他们的 Paillier 参数。

在该协议期间,诚实的参与者 $P_j$(受害者)将使用攻击者的密钥加密他们的份额。他们发送:

$C = V^x \cdot \text{Enc}(\beta) \mod N^2$

其中 $x$ 是受害者的秘密份额,$\beta$ 是一些随机数。Paillier 加密是 $\text{Enc}(\beta) = (1+N)^\beta r^N$,其中 $r$ 是随机数。

现在攻击者将 $C$ 对 $q$ 取模:

$C \equiv V^x \cdot (1+N)^\beta r^N \equiv V^x \cdot r^N \pmod{q}$

由于 $N \equiv 0 \pmod{q}$,因此 $(1+N)^\beta$ 项对 $q$ 取模后消失了。接下来,攻击者计算:

$C^{p+1} \equiv (V^x r^N)^{p+1} \equiv V^{x(p+1)} r^{N(p+1)} \pmod{q}$

以下是关键观察:在 $\mathbb{Z}_q^*$ 中,阶数为 $q-1 = 2p$。每个平方数(如 $V$)的阶数都除以 $p$,并且根据费马小定理,$r^{pq} \equiv 1 \pmod{q}$。因此:

$C^{p+1} \equiv V^{x(p+1)} \equiv V^x \pmod{q}$

因为 $V^p \equiv 1 \pmod{q}$。

现在攻击者有了 $V^x \bmod q$。由于 $q = 2p+1$ 足够小(攻击者选择了它),他们可以暴力破解离散对数以恢复 $x \bmod q$。

扩大攻击范围

如果 $q$ 太大而无法进行单次暴力破解,攻击者可以使用许多此类对的乘积:$N = p_1q1 \cdots p{16}q_{16}$,其中每个 $q_i = 2p_i + 1$ 并且每个 $p_i$ 都很小(例如,16 位)。他们并行运行攻击以恢复每个 $i$ 的 $x \bmod q_i$,然后使用中国剩余定理重构 $x$。

使用 16 个此类对和 16 位素数,攻击者可以在普通硬件上在几个小时内恢复 256 位的秘密份额。一旦他们拥有一个份额,他们就可以单方面签署任意交易,从而完全破坏阈值属性。

此漏洞由 Fireblocks 在对 BitGo 实现的安全分析期间发现,并在他们的博客文章中进行了描述。

修复: 包括缺少的证明。必须证明 Paillier 模数 $N_i$ 是双素数(正好是两个大素数的乘积),并且每个参与者都必须证明他们知道自己的份额 $x_i$。

不是那么秘密的因式分解

有时攻击不是缺少检查,而是实现让证明者获得了他们本不应该拥有的知识。这个双人 ECDSA 重共享协议中的漏洞显示了知道“太多”可能会被武器化。

重共享协议

密钥重共享是生产 TSS 钱包中的标准做法,你会定期轮换份额以限制暴露。该协议看起来很简单:

  1. Alice 生成一个新的 Paillier 密钥对 $(n', sk')$
  2. Alice 在她的新密钥下发送一个新的密文 $c' = \text{Enc}(x_1')$,其中 $x_1' = x_1 + r$(她的旧份额加上随机性)
  3. Alice 以零知识证明 $c'$ 加密的值与她的旧密文 $c = \text{Enc}(x_1)$ 线性相关,即 $c' = c \oplus \text{Enc}(r)$(使用 Paillier 同态)

Bob 接收 $c'$,验证证明,并将他自己的份额更新为 $x_2' = x_2 - r$。组合秘密保持不变:$x_1' + x_2' = (x_1 + r) + (x_2 - r) = x_1 + x_2 = x$。

问题

第 3 步的零知识证明需要两个模数 $n_1$ 和 $n2$。文献中的安全假设:"设 $n_1$ 是一个大的合数,其因式分解是 Alice 和 Bob 未知的,并且 $n2$ 是另一个大的数,素数或合数,其因式分解是 Alice 知道或未知的。"

但在这个实现中,Alice 知道两个因式分解,她自己生成了两个 Paillier 密钥。她现在可以将重共享协议变成一个逐位泄露 Bob 份额的 oracle。

攻击

Alice 操纵她的新份额为 $x_1'' = x_1' + kq$,其中选择 $k$ 以便:

$|n_i| - q \leq x_1 + kq < |n_i|$

Bob 没有发现任何错误。他将 Alice 的恶意份额与他旋转的份额结合起来:

$x_{12} = x_1'' + x_2' = (x_1' + kq) + (x_2 - r) = x_1 + x_2 + kq$

当他们运行签名协议时,Bob 将他的部分签名发送给 Alice。Alice 解密组合结果并将其对 $q$ 取模。但在 Paillier 解密期间,首先会发生对 $n_i$ 取模的约简。这就是 oracle 发挥作用的地方:

  • 如果 $x_2 < n_i - kq - x_1$,则模 $n_i$ 的约简不会回绕。最终签名验证正确。
  • 如果 $x_2 \geq n_i - kq - x_1$,则模 $n_i$ 的约简会更改该值。签名验证失败。

Alice 刚刚了解到 $x_2$ 是否跨越了特定阈值。她记录了结果。

提取秘密

通过使用不同的 $k$ 和 $n_i$ 值重复此重共享和签名周期,Alice 会逐渐缩小 Bob 的份额。每次成功或失败的签名都会揭示有关 $x_2$ 的一位信息。经过足够的迭代(与 $x_2$ 的位长度成正比),Alice 会重构 Bob 的整个份额。

一旦她同时拥有 $x_1$ 和 $x_2$,阈值属性就会崩溃。Alice 可以单方面签署任何交易。

此问题是在对生产双人 ECDSA 实现的分析期间发现的,并在论文“攻击阈值钱包”中进行了详细说明。

修复

ZK 证明必须强制 Alice 不知道 $n_1$ 的因式分解。更好的是,库维护人员做了最合理的选择:他们用完整的双人密钥生成协议替换了易受攻击的重共享。Alice 和 Bob 运行一个抛Coin程序来刷新他们的份额并重复 DKG 中的所有 ZK 证明。它速度较慢,但它是正确的,并且不会给 Alice 一个 oracle。

模糊的哈希编码

Fiat-Shamir 应该很简单:哈希转录,将输出用作你的挑战。但是,如果你对如何编码转录不严谨,则会为证明者提供他们本不应该拥有的自由度。

缺陷

当通过 Fiat-Shamir 将交互式证明转换为非交互式证明时,你需要将多个值(群元素、整数、承诺等)哈希在一起。一种幼稚的方法:将所有内容与分隔符连接起来。

$H(\alpha_1 ,|, D ,|, \alpha_2 ,|, D ,|, \ldots ,|, D ,|, \alpha_n)$

其中 $D$ 是一些分隔符字节序列(例如,|| 或空字节)。问题:在“打开”阶段,存在歧义。第一个元素是 $\alpha_1$,还是 $\alpha_1 ,|, D ,|, \alpha_2$? 如果证明者可以移动边界,他们可以操纵输入的哪些部分被解释为哪些值,而无需更改哈希输出。

示例:离散对数证明

考虑一个简单的离散对数证明(例如阈值签名 ZK 子协议中使用的证明):

  1. 证明者承诺:选择随机 $\rho$,发送 $\alpha = h^\rho$(或 $\alpha = h^\rho g^{-1}$,具体取决于策略)
  2. 挑战:计算挑战位 $c = H(g, h, N, \alpha_1, \alpha2, \ldots, \alpha\lambda)$,其中 $\lambda$ 是重复次数
  3. 响应:对于每次迭代 $i$,发送 $\tau_i = \rho_i + c_i \cdot \text{secret}$

该证明重复 $\lambda$ 次以放大可靠性。每次迭代贡献一个挑战位 $c_i$。

攻击

攻击者(扮演证明者的角色)想要伪造 $\log_h g$ 的证明,而不知道它。方法如下:

  1. 猜测挑战位:攻击者预测挑战 $c = (c1, \ldots, c\lambda)$ 中将出现多少个 1 位。假设他们猜了 $k$ 个 1。

  2. 制作模糊的承诺:对于每次迭代 $i$,攻击者准备两个可能的 $\alpha$ 值:

    • 如果他们计划回答为 $c_i = 0$,则为 $\alpha^1 = h^{\rho_i}$
    • 如果他们计划回答为 $c_i = 1$,则为 $\alpha^2 = h^{\rho_i} g^{-1}$
  3. 模糊地承诺:攻击者发送一个看起来像 $\alpha_1 ,|, D ,|, \alpha_2 ,|, D ,|, \ldots$ 的字节流,但可以用多种方式解析。具体来说,他们构造它,以便在哈希之后,他们可以根据生成的挑战位“洗牌”哪些字节属于哪些 $\alpha_i$。

  4. 在看到挑战后重新解释:一旦哈希输出挑战位 $c_i$,攻击者就会追溯决定如何解析他们的承诺。如果 $c_i = 0$,他们声称他们发送了 $\alpha^1 = h^{\rho_i}$。如果 $c_i = 1$,他们声称他们发送了 $\alpha^2 = h^{\rho_i} g^{-1}$。在这两种情况下,他们都会响应 $\tau_i = \rho_i$。

  5. 验证通过:验证者检查 $h^{\tau_i} \stackrel{?}{=} \alpha_i \cdot g^{c_i}$。如果攻击者正确洗牌:

    • 对于 $c_i = 0$:$h^{\rho_i} = \alpha^1$ ✓
    • 对于 $c_i = 1$:$h^{\rho_i} = \alpha^2 \cdot g = (h^{\rho_i} g^{-1}) \cdot g$ ✓

关键的见解:只要字节流中的 aaa Token数量与哈希函数期望的数量匹配(无论它们如何解释),哈希保持不变。攻击者重新排列 $(\alpha, \alpha, \ldots, \alpha, \beta, \beta, \ldots, \beta)$ 以匹配挑战位,并且所有内容都通过验证。

此漏洞($\alpha$-shuffle 攻击)由 Verichains 在 Binance 的 tss-lib、Multichain 和其他一些实现中发现。受影响的实现和完整的技术细节在他们的博客文章中进行了描述。

修复 使用明确的编码。不要只使用分隔符连接。像 FROST 和 CGGMP20 这样的现代协议要求使用特定的序列化格式,以避免这种情况。不要推出你自己的连接方案。

可靠性不足

记住我们刚才讨论的离散对数证明吗?它重复 $\lambda$ 次,可靠性误差为 $2^{-\lambda}$。为了在不知道离散对数的情况下伪造证明,攻击者需要正确猜测所有 $\lambda$ 个挑战位,概率为 $2^{-\lambda}$。 如果你想要 128 位安全性,则需要 $\lambda = 128$ 次重复。很简单。

攻击

一些生产实现使用 $\lambda = 80$,甚至 $\lambda = 32$。当 $\lambda = 80$ 时,攻击者有 $2^{-80}$ 的机会伪造证明,这在足够的计算能力下是可行的。当 $\lambda = 32$ 时,这很简单。

TSSHOCK 论文发现库只是……将参数设置得太低了。不需要巧妙的密码学见解。只需暴力破解挑战空间,直到你幸运为止。

此攻击( c-guess)也由 Verichains 在 Multichain 的实现中发现。

修复: 设置 $\lambda \geq 128$。阅读安全性证明。使用论文推荐的参数,而不是在实现期间看起来“足够好”的任何参数。

不存在的逆元

所以你阅读了上一节并认为,“让我们使用更大的挑战空间,而不是重复协议 128 次,从所有 256 位字符串中采样 c 而不是仅仅 0,1{0,1}0,1。" 听起来合理。一轮,更大的挑战,同样的安全性。除了它在复合阶群中不起作用。

缺陷

在标准 dlnproof 中,你要证明你在阶数为 $\text{ord}(g)$ 的群中知道 $\log_h g$。协议:

  1. 证明者承诺 $\alpha = g^\rho$,其中 $\rho$ 是随机的
  2. 挑战 $c \in {0, 1, \ldots, 2^{256}-1}$(范围很大,单轮)
  3. 证明者响应 $\tau = \rho + c \cdot \text{secret}$

验证者检查 $g^\tau \stackrel{?}{=} \alpha \cdot h^c$。 这在素数阶群中效果很好。但在复合阶群(如 RSA 模数 $\tilde{N}$ 的 $\mathbb{Z}_{\tilde{N}}^*$)中,情况会出错。

攻击

假设攻击者想要伪造 $\log_h g$ 的证明,而此离散对数不存在。具体来说,设 $h = g^e$,其中 $e$ 是 $\text{ord}(g)$ 的一个小除数,并且 $2 \mid \text{ord}(g)$:$\logh g = \frac{1}{e}$ 在 $\mathbb{Z}{\text{ord}(g)}$ 中不存在,因为 $e$ 对 $\text{ord}(g)$ 没有乘法逆元(因为 $\gcd(e, \text{ord}(g)) \neq 1$)。

攻击者按如下方式进行:

  1. 暴力破解随机的 $\rho$ 值,直到生成的挑战 $c = H(\ldots)$ 可被 $e$ 整除。由于 $c$ 是 256 位,因此 $e \mid c$ 的概率约为 $\frac{1}{e}$。如果 $e$ 很小(例如,32 位),则这需要的时间可以忽略不计。
  2. 一旦 $e \mid c$,攻击者就会构造证明:$(\alpha, \tau) = (g^\rho \bmod \tilde{N}, \rho + \frac{c}{e})$。
  3. 验证:

$g^\tau = g^{\rho + c/e} = g^\rho \cdot g^{c/e} = \alpha \cdot (g^e)^{c/e} = \alpha \cdot h^c$

该证明通过验证,即使攻击者不知道 $\log_h g$。

为什么有效 在素数阶群中,除非 $e$ 有逆元,否则 $\frac{c}{e}$ 的定义不明确。但在具有将阶数除尽的小因子 $e$ 的复合阶群中,攻击者可以按该因子来“拆分”挑战,并伪造不存在的离散对数的证明。

这是 Verichains' TSSHOCK 套件中的第三次攻击 (c-split),并影响 Axelar、ZenGo 和 ING。完整详细信息可在他们的文章中找到。

修复 任选一种:

  • 坚持使用素数阶群(如椭圆曲线子群,而不是 $\mathbb{Z}_{\tilde{N}}^*$)
  • 使用具有二进制挑战的多轮版本(80–128 轮)
  • 如果必须使用具有大挑战的复合阶群,请确保群阶数为素数,或者在运行协议之前证明所有相关的离散对数都存在

这就是现代阈值 ECDSA 协议在椭圆曲线(素数阶子群)上进行签名生成的原因,并且仅在具有适当参数检查的辅助 ZK 证明中谨慎使用 RSA 群(复合阶)。

小型 Paillier 攻击

即使包含范围证明,实现错误仍然会导致问题。此攻击利用了 Paillier 模数大小本身缺少验证的漏洞。

设置

在使用范围证明的 MtAwc 中,Bob(具有秘密 $b$ 的一方)发送一个零知识证明,其中包括:

  1. 一个挑战 $e \in \mathbb{Z}_q$(通过 Fiat-Shamir 计算)
  2. 一个随机的盲化值 $\gamma \in \mathbb{Z}_N^*$(其中 $N$ 是 Alice 的 Paillier 公钥)
  3. 一个证明元素 $t_1 = e\beta' + \gamma$(在整数上计算),其中 $\beta'$ 是来自 MtA 协议的 Bob 的秘密

该证明应该隐藏 $\beta'$。安全性取决于 $\gamma$ 的大小是否足以在除以 $e$ 时掩盖 $\beta'$。

攻击

攻击者 (Alice) 生成一个略小于 $2^{256}$ 的 Paillier 模数 $N$。如果协议没有明确验证密钥大小,则会被通过。

现在看看当 Bob 计算 $t_1 = e\beta' + \gamma$ 时会发生什么:

$\frac{t_1}{e} = \beta' + \frac{\gamma}{e}$

通常,$\gamma \in \mathbb{Z}_N^*$,其中 $N \approx 2^{2048}$ 并且 $e \approx 2^{256}$,因此 $\frac{\gamma}{e}$ 足够大,可以在除以 $e$ 时隐藏 $\beta'$。但是如果 $N \approx q \approx 2^{256}$,则 $\gamma$ 仅约为 $2^{256}$ 位。现在:

  • 以 $\frac{1}{2}$ 的概率,我们有 $\frac{\gamma}{e} < 1$(即,$\gamma < e$)
  • 以 $1 - 2^{-16}$ 的概率,我们有 $\frac{\gamma}{e} < 2^{15}$(即,小于 15 位)

这意味着 $\beta' \approx \lfloor \frac{t_1}{e} \rfloor$ 的概率很高。盲化几乎完全消失。

提取秘密

Alice 还知道使用 $N \approx q$ 时,如果她在 MtA 协议中将她的输入 $a = 1$,则解密的值 $\alpha' = b + \beta'$(在整数上)会落入以下两个范围之一:

  1. 如果 $b + \beta' < N$:没有模约简,因此 $\alpha = b + \beta'$
  2. 如果 $b + \beta' \in [N, 2N)$:发生约简,因此 $\alpha = b + \beta' - N$

Alice 知道 $\alpha$(她解密了它)并且可以从证明中近似 $\beta' \approx \lfloor \frac{t_1}{e} \rfloor$。她尝试了几个候选者:

$\beta \in {\lfloor \tfrac{t_1}{e} \rfloor \bmod q, , \lfloor \tfrac{t_1}{e} \rfloor - 1 \bmod q, , \lfloor \tfrac{t_1}{e} \rfloor - 2 \bmod q, \ldots}$

对于每个候选者 $\beta$,她计算了 Bob 的秘密 $b$ 的两种可能性:

  1. $b^{(1)} = \alpha - \beta \bmod q$(假设没有回绕)
  2. $b^{(2)} = \alpha - \beta + N \bmod q$(假设有回绕)

她通过验证 $g^{b^{(1)}} \stackrel{?}{=} g^b$ 或 $g^{b^{(2)}} \stackrel{?}{=} g^b$ 来检查哪个是正确的,其中 $g^b$ 是 Bob 的公钥(所有人都可以访问)。

其中一个将匹配。Alice 在单个签名中恢复了 Bob 的秘密 $b$。

为什么有效

两个错误复合在一起:

  1. $\gamma$ 的范围错误:ZK 证明规范声明 $\gamma$ 应该从 $\mathbb{Z}_{q^2 N}$ 中采样,而不是从 $\mathbb{Z}_N^$ 中采样。使用 $\mathbb{Z}_N^$ 会破坏诚实验证者的零知识性,无论 Paillier 模数大小如何,除以 $e$ 都会泄露 $\beta'$ 的位。
  2. 缺少 Paillier 大小检查:即使范围错误,如果 $N$ 被正确验证为 $\geq 2^{2048}$,则泄漏也可以忽略不计。但是如果没有验证,Alice 可以选择 $N \approx q$ 并完全消除盲化。

总之,这些错误将单个签名变成完整的密钥提取。

这是来自“Alpha-Rays:对阈值 ECDSA 实现的密钥提取攻击”的小型 Paillier 攻击。该论文报告了对比南的 tss-lib 和多个分支的影响。

不正确的广播通道

MPC 中的广播通道不仅仅是一个锦上添花的功能,它们是安全要求。 假设是当一方广播一个值时,每个人都收到相同的值。 但是一些实现偷工减料,将“广播”实现为一个循环,该循环向每个参与者发送单独的消息。 这不一样,原因如下。

对可验证秘密共享的攻击

回想一下,在 Feldman VSS(Pedersen DKG 的基础)中,庄家通过发布每个系数的承诺 $y_i = g^{a_i}$ 来承诺一个多项式 $p(x) = a_0 + a1 x + \cdots + a{t-1} x^{t-1}$。 每个参与方 $P_j$ 收到一个份额 $s_j = p(j)$ 并使用以下公式验证它:

$g^{sj} \stackrel{?}{=} \prod{i=0}^{t-1} y_i^{j^i}$

这些承诺 $y_0, y1, \ldots, y{t-1}$ 应该被广播,每个人都看到相同的向量。

打破广播假设

假设攻击者想要伪造一个他们不知道的秘密 $a_0$ 的共享。 具体来说,他们希望每个人都接受 $y_0 = g^{a_0}$ 作为对秘密的承诺,但攻击者实际上并不知道 $a_0$。

如果广播实现为单独的消息,则攻击者可以向每个参与方发送不同的承诺

  1. 创建一个截断的多项式:选择 $p(x) = a_2 x^2 + a3 x^3 + \cdots + a{t-1} x^{t-1}$(省略 $a_0$ 和 $a_1$)
  2. 发送份额:计算每个参与方 $P_j$ 的 $s_j = p(j)$ 并发送它们(这些是截断多项式的有效评估)
  3. 诚实地计算大多数承诺:对于 $i \geq 2$,计算 $y_i = g^{a_i}$(这些对于每个人都是相同的)
  4. 为每个参与方自定义 $y_1$:对于每个参与方 $P_j$,计算一个个性化的承诺:

$y_{1,j} = {(y_0^{-1})}^{j^{-1}}$

  1. 发送不同的向量:将向量 $y0, y{1,j}, y2, \ldots, y{t-1}$ 发送给参与方 $P_j$

image

为什么验证通过

参与方 $P_j$ 检查:

$g^{sj} \stackrel{?}{=} \prod{i=0}^{t-1} y_i^{j^i}$

展开右侧:

$\prod_{i=0}^{t-1} y_i^{j^i} = y0^{j^0} \cdot y{1,j}^{j^1} \cdot y2^{j^2} \cdots y{t-1}^{j^{t-1}}$

替换 $y_{1,j} = {(y_0^{-1})}^{j^{-1}}$:

$= y_0 \cdot (y_0^{-j^{-1}})^j \cdot y2^{j^2} \cdots y{t-1}^{j^{t-1}} = y_0 \cdot y_0^{-1} \cdot y2^{j^2} \cdots y{t-1}^{j^{t-1}} = y2^{j^2} \cdots y{t-1}^{j^{t-1}}$

$y_0$ 和 $y_1$ 项被抵消了! 并且由于 $s_j = p(j) = a2 j^2 + \cdots + a{t-1} j^{t-1}$,我们有:

$g^{s_j} = g^{a2 j^2 + \cdots + a{t-1} j^{t-1}} = y2^{j^2} \cdots y{t-1}^{j^{t-1}}$

每个参与方的检查都通过了,即使攻击者不知道 $a_0$ 或 $a_1$。 每个参与方都认为他们拥有与 $y_0 = g^{a_0}$ 对应的秘密的有效共享,但攻击者从未计算出该秘密的一致份额。

此攻击在 Jake Craige 的文章“针对不良广播通道的 VSS 伪造”中进行了描述。

结束语

阈值方案仍然是我们用于分布式信任的最佳工具之一,但“可证明的安全协议”和“可用于生产的实现”之间的差距是真实存在的。 这里介绍的攻击并非详尽无遗,它们只是审计员发现的一个快照。

很明显:缺少检查和跳过的证明。 在将阈值签名发布到生产环境之前,请审计这些类型的错误。 检查参数大小。 验证输入。 并包括论文中指定的所有证明。 数学有效。 确保你的实现也有效。
  • 原文链接: hexens.io/blog/mpc-attac...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

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