这篇文章深入探讨了多方计算(MPC)钱包的工作原理、组成部分以及安全性挑战。文章从传统加密学的基础知识入手,解释了如何通过MPC协议安全地管理私钥,同时揭示了实际的攻击案例以强调实现过程中可能存在的安全漏洞。
加密钱包的目标是确保用户的币安全且稳固,MPC 钱包也是如此。然而,偶尔,当这些钱包使用不当时,其安全性可能会受到破坏。让我们深入了解 MPC 钱包的流程及一些实际案例,其中这些钱包遭到攻击。
在1980年代末,一位名叫安德鲁·姚(Andrew Yao)的计算机科学家提出了一种首个双方计算方案。这被称为姚的百万富翁问题,因为他的方案可以在两个人的互动中找出谁更富有,而无需披露他们的实际财富。
在传统的密码学中,个人保留私人密钥,同时为全世界发布其公钥。根据公钥,任何人都可以加密一条消息并发送给个人,以使其能够解密和阅读消息。传统密码学的目标是确保加密消息的安全性和完整性,以防窃听或中间人攻击。
与传统密码学不同,多方计算 (MPC) 旨在允许一组个人相互分享信息,而不揭示每个人的私人数据的实际值。它允许用户之间合作进行计算,而不透露任何敏感信息。在数学术语中,MPC 允许一组人集体评估一个函数,而无需共享每个人的输入。
让我们用一个例子来说明。有20名学生刚刚完成期末考试并收到了分数。出于对学生低分的同情,教授愿意提供额外的学分,如果他们能够在不分享分数的情况下找出考试的最低分数。为了提高他们的成绩,学生们需要评估这样的函数
F(a1,a2,...,a20)=min(a1,a2,...,a20)
其中 a1,a2,…,a20 是每个学生的对应测试分数。解决此问题的一个简单方法可能是将他们的分数揭示给外部第三方,由该第三方查看每个测试并迅速找出答案。然而,由于学生们都不信任教室外的人,他们需要另一种方法来解决这个问题,而不需要外部方。
使用 MPC 协议,学生们将能够评估并了解到考试的最低分数。此外,在函数的输出中,其余19名没有最低分数的学生将无法获得任何额外信息,关于哪个学生的分数匹配函数的输出。
现在我们已经了解了 MPC 的基础知识,我们可以开始理解它是如何纳入 MPC 钱包中的。加密钱包有很多不同类型。大多数加密钱包可以分为以下两类:
但是,MPC 钱包与这些类别有所不同,因为 MPC 协议依赖于多个用户。每个人都有自己私钥的一部分副本,但从未拥有完整的私钥。私钥永远不会存储在一个地方。当所有用户使用他们自己的一部分私钥进行计算时,将生成一个签名。通过将每个签名拼接在一起,可以计算出一个可用的私钥签名副本。即便如此,每个人也无法计算出实际的私钥,因为每个部分由不同的用户持有。
传统上,加密钱包为每个用户生成一组密钥(私钥和公钥)。这使得攻击者能够通过攻击一个用户来破坏一个钱包。对于 MPC 钱包,由于每个钱包持有者只有完整私钥的一小部分,因此攻击者必须攻克数量超过密钥设定阈值内的多个持有者。
通过将私钥分散到大量钱包中,MPC 钱包大大增强了存储币的安全性。这也允许用户放弃硬件钱包,因为 MPC 钱包的安全性足够高,并且便于转移币和支付。
尽管许多 MPC 钱包声称使用 MPC,但现在大多数钱包只利用多方的阈值签名,而不是完整的通用 MPC 功能。如今的 MPC 钱包通常包含三个主要步骤:密钥生成、签名和验证,通常使用椭圆曲线数字签名算法 (ECDSA)。密钥生成为签名创建私钥和公钥对。签名和验证过程确保钱包中每笔交易的有效性。
在这三个步骤中,最重要的步骤就是密钥生成,这一步必须为所有参与者创建一对公钥和私钥。私钥还必须分割成多个份额,以便每个参与者收到一个单独的份额,而不对其他人的份额获得任何信息。
在单个 ECDSA 设置中,密钥生成如下进行:
在 MPC 钱包的单个 ECDSA 设置中,问题在于这是一个单点故障。如果每个用户都有私钥的副本,那么每个用户就成为整个协议的单点故障。通过生成私钥的份额并将其在每个参与者之间分割,参与者就不再是单点故障。
在阈值 ECDSA 设置中,步骤如下:
为什么使用 Paillier 密钥对?Paillier 加密用于对另一方的私钥份额进行运算,而不透露该份额。Paillier 具有加法和乘法同态特性。
将两个密文相乘后的解密结果为 m1 + m2,其中 m1 和 m2 是密文的明文。
Dec(Enc(m1)∗Enc(m2))=m1+m2
将一个密文提高到另一个明文的幂后的解密结果为 m1∗m2,其中 m1 和 m2 是密文的明文。
Dec(Enc(m1)m2)=m1∗m2
每当用户想要发起一笔交易或其他指令时,将为所有其他用户创建一个签名以进行验证和批准。如果签名有效,那么该指令将被批准。否则,如果签名无效,则指令不会被批准,该用户存在可疑之处。
通常,MPC 钱包使用 ECDSA 进行签名。从 SSSS 的密钥生成和密钥共享过程中,私钥保持秘密,不可被攻击者恢复。ECDSA 签名发布给 MPC 协议中的所有方,所有人都可以验证。
通常,ECDSA 的工作流程如下:
任何拥有公钥的人都可以使用 r 和 s 的值来验证消息的签名。
在阈值设置的 ECDSA 中,参数略有调整。在双方 ECDSA 签名方案中,两个参与者都有要签名的消息以及椭圆曲线上的共同公点。共同公钥通过椭圆曲线 Diffie-Hellman (ECDH)密钥交换生成,两个参与者计算自己的秘密以获取共享点。
参与者2还将通过 Paillier 加密系统获得加密的 x1,使参与者2能够对 x1 的加密版本进行计算,而参与者1进行解密。Paillier 加密的私钥由参与者1生成并保持秘密。消息的哈希表示为 m’ ,曲线的阶表示为 q。
签名协议如下:
零知识证明是为了确保 R 点是根据生成器 G 的倍数计算的,而不是恶意构建的点。
MPC 协议中的所有方都有访问用户为签名发布的公钥。验证过程因签名算法而异,但每个用户都可以使用发布的公钥单独验证签名。
对于 ECDSA,只要签名和消息被发布,任何人都可以使用公钥来验证签名的有效性。在正常情况下和双方 ECDSA 协议中,验证算法是相同的。算法如下:
MPC 钱包并不总是安全,这在很大程度上归因于实施缺陷。即使 MPC 协议被证明是安全的,不正确的实施也可能导致漏洞。让我们看看一些现实世界的案例,了解 MPC 钱包是如何被利用的。这些漏洞是重要的,以便在未来可以避免。
2023年4月9日,FireBlocks 发布了现称为 BitForge 的漏洞 (CVE-2023-33241)。通过利用 Paillier 模数中的小因子,对 GG18、GG20 和 Lindell17 MPC 协议的攻击影响了超过15款最受欢迎的加密钱包。
BitForge 漏洞发现,某些使用阈值 ECDSA 方案的钱包没有提供零知识证明,未检查 N 是否存在小因子,或者 N 是否在 Paillier 密钥生成过程中是双素数。因此,作为 MPC 协议中的恶意方,Paillier 密钥的生成可以被更改,使得 N 由多个小素数构成。在没有 N 的安全性零知识证明的情况下,接收方不会注意到被腐败的 N 的不安全性。
对于 2,048 位 Paillier 模数和在 GG18 和 GG20 中实施的 256 位机密,攻击方可以构造出一个模数,使得 N=p1∗p2∗...p16∗q,其中 p1...p16 是 16 位素数,而 q 是一个大素数,从而使 N 变成 2,048 位。
在高层面上,通过向另一方发送这些特定构造的 N 进行签名,可以分部分地提取机密。从收到的签名中,如果机密为 x,则攻击者可以计算出 x mod pn,其中 pn 是模数中的 16 位素数之一。通过 16 个“错误”的签名,攻击者可以通过 CRT 求解一个同余系统,以重构机密。
在实际攻击中,漏洞在于 GG18/GG20 方案的无知线性评估 (OLE) 部分,幸运的是,在 OLE 中使用 Paillier 进行加密和解密。在 OLE 中,一个值 γ 将从参与者1获取,机密 x 和 β 将从参与者2获取。此交换的最终结果将为参与者1提供一个值 α,其中 α+β=γ∗x mod N。在交换中,
在恶意设置中,若没有零知识证明确认 N 是双素数且具有较大因子,参与者1可以构造较弱的 N。参与者1 发出的将不再是 γ,而是 N/pi 其中 pi 是构成 N 的 16 位素数之一。因此,最终参与者1收到的解密值将变为:
α≡x∗N/pi−β mod N
由于 β 约为 1,024 位,N/pi 约为 2,000 位,因此可以忽略 β,得到的方程可以求解 x mod pi:
x≈(α−(α mod N/pi))/(N/pi) mod pi
通过遍历 N 中的所有 16 位素数,参与者1可以构造一个包含方程形式的同余系统,求解 x≈(α−(α mod N/pi))/(N/pi) mod pi 对于所有 i 从 1–16。这个系统可以通过 CRT 求解,以恢复实际的 256 位机密。
除了 BitForge 漏洞外,公开已知的针对 MPC 钱包的严重攻击数量不多。然而,利用 SSSS 进行密钥交换的非 MPC 钱包曾遭受过一些攻击,尤其是 Bitcoin Armory 攻击。Bitcoin Armory 曾是一款流行的加密钱包,用于存储比特币。然则,在 SSSS 实现中,他们反复将 实际秘密 哈希以生成拉格朗日多项式的系数。
coeff0 = SECRET
coeff1 = Hash(coeff0)
coeff2 = Hash(coeff1)
...
...
coeffn = Hash(coeffn-1)
更糟糕的是,每个插值点的给定份额都是多项式的系数。每位用户将接收一个系数(而非秘密)和具系数作为值的插值函数:
share1 = (coeff1, f(coeff1))
通过获得对应于第一个系数的份额(在秘密之后),x 坐标可以被多次哈希以重建多项式中的其余系数。然后,通过已知系数的插值给定点,从而可以找到秘密,并恢复私钥。
以下是在 SageMath 中实施的攻击(假设 SHA-256 为哈希,并已知多项式的度数),
from hashlib import sha256
from Crypto.Util.number import long_to_bytes
p = # 在拉格朗日多项式共享方案中的素模数
point = (coeff1,f(coeff1)) # 这些值的具体内容
coefficients = [point[0]]
for i in range(2): # 秘密多项式的度数 - 1
coefficients.append(int(sha256(long_to_bytes(coefficients[i])).hexdigest(), 16))
P.<x,y> = PolynomialRing(GF(p))
f = x
for i in range(len(coefficients)):
f += coefficients[i] * y**(i+1)
f = f - point[1]
priv_key = f(x, point[0]).univariate_polynomial().roots()[0][0]
print(priv_key)
虽然 MPC 钱包目前是最安全的加密钱包,但没有什么是完全安全的。重要的是要牢记,没有任何钱包可以完全保证用户资金的安全性。经证明安全的 MPC 协议在现有 MPC 钱包中的错误实施,仍然可能导致攻击者利用的漏洞。
欲获取有关构建安全 MPC 协议的更深入指南,请查看 Zellic 联合创始人 Luna 关于从零开始构建 MPC 的博客。
Zellic 专注于保护新兴技术。我们的安全研究人员发现了最有价值目标的漏洞,从财富500强到 DeFi 巨头。
开发者、创始人和投资者信任我们的安全评估,能够快速、自信并且没有重大漏洞。凭借我们在现实世界攻防安全研究中的背景,我们发现他人不曾察觉的漏洞。
请联系我们,进行一次比其他审计更优质的审计。真实审计,而非橡皮图章。
- 原文链接: zellic.io/blog/mpc-walle...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!