作者:Jameson Lopp
来源:https://blog.keys.casa/shamirs-secret-sharing-security-shortcomings/
在比特币中,最微小的错误也可能引发灾难性的后果;因此,致力于私钥安全的人们孜孜不倦地尝试消除自己构建的系统中的 一切单点错误。
自然地,将一个私钥分割成多个碎片、日后再通过一个仪式将它们组合起来的想法,因此变得有吸引力。
显然,在密码货币领域,人们多次实现过 Shamir 私钥分割(SSS)的许多变种,只是开发者 后来 才意识到,额外的复杂性最终降低了系统的安全性。
Shamir 的方法使得一个私钥可以安全地分割,并且全部 n 个碎片中只需要有 k 个既可以重构出原本的私钥,但攻击者即使拿到了 k-1 个碎片,也不会曝光关于原本私钥的任何信息。针对比特币的私钥分割是通过分割一个密钥(不论是单个的私钥还是能够生成许多确定性私钥的种子)成许多碎片,使得这些碎片的子集就可以重新组合、恢复密钥并使用该密钥来签名交易。
“作为一次性密码本的普遍化,Shamir 私钥分割,如果能完美地实现,将具有实现信息理论安全性的罕见属性:它强大到能对抗无限计算力的敌手。这种 ‘完美安全性’ 增加了它的神秘之美,但这种属性根本没有实际的用途,而且似乎转移了人们的注意力:大部分的 SSS 尝试都是如此地弱,以至于三岁小孩也能破解。就像一次性密码本以及大部分其它密码系统,SSS 的安全性是非常脆弱的。”
—— Gregory Maxwell
私钥分割可以作为多签名(multisig)的一种替代,但研究了它在 Casa 中的实际应用之后,我们选择不实现 Shamir 私钥分割方案,因为它会让客户暴露在更多风险之中。
本文剩下的部分将更详细地解释这些风险,例如:
- 单点故障
- 碎片撤回难题
- 实现的复杂性
- 缺乏实现标准
- 社交恢复问题
- 可审计性
- 碎片完整性
- 旁路攻击
单点故障
为了通过 SSS 分割私钥,必须先有一台设备持有完整的私钥;而且,为了签名私钥,它也需要在某一台私钥上完成重构。如果这个设备在此期间被劫持了,那么用户的资金就有可能被盗。SSS 可以抵御的大部分威胁模型都可以通过多签名来抵御,并且效果更好,因为在一台设备上重新合并私钥碎片会使私钥暴露给恶意软件以及设备的恶意用户,而这本身是最需要抵御的重大威胁。如果使用了多签名,你可以使用地理上分散的硬件和软件、消除这样的单点故障。
没有碎片撤回机制
在 SSS 中,无论要对分割好的碎片作何种结构性变动,都必须取得阈值数量的碎片以重构私钥,然后通过新的分割操作替换所有的碎片。这给收到攻击后的快速恢复,甚至是简单的系统更新,带来了极大的困难。此外,碎片的处理者(也就是私钥的拥有者)可能会选择提前生成许多套碎片,并将额外的碎片储存起来,以便快速重新发布碎片,但是,这又重新产生了一种单点错误:所有的碎片都保存在发布者手上。碎片的任何变更都需要所有的碎片持有者一起更新,随着碎片持有者数量的增加,仪式会变得更加繁琐。
相反,多签名的用户可以作废单个丢失的私钥并替换它,实际上就是 “轮换” 它。其它密钥可以保持不变,加入新密钥就可以构造一个新的多签名装置和地址集合。
复杂性是安全性的敌人
“我认为,Shamir 私钥分割(以及许多其它
东西,例如 RNG),都受困于一个问题:它们刚好复杂到人们会兴致勃勃去实现它们,但通常没什么很好的理由;同时它们又是如此复杂(或者说为之投入大量时间的理由很少),使得实现通常都很差。”
—— Gregory Maxwell
Maxwell 在 Armory 的 “分割型备份” 中发现了一个漏洞。这种特性使用了一种定制化的 SSS 实现。
这种实现没有使用随机数生成器,而是重复对数据运行哈希计算,所以,这种确定性跟其它错误相结合,让整套方案完全被打破了。结果是拿到了第一个碎片以及其它任意一个碎片(准确来说,是第 N 个碎片和其它任意 N 个碎片)的攻击者,可以无视阈值要求,恢复出完整的私钥。
另一种已被发现有问题的 SSS 实现是 HTC Exodus 手机所使用的,在备份用户的私钥时,它会按 3-of-5 的方案,将碎片分发给通讯录里的人,但它没能实现自己宣传的安全模型:
“可以证明的是,无论检索出哪个碎片,系统都总会有 2 到 256 种解。为了测试返回的解,我们使用了之前提到的 DRBG 的特性:一次多项式系数的字节表示,必须等于二次多项式系数的 SHA-256 值。给定两个碎片,一秒内就可以就求出并测试系统的所有解。私钥是从两个碎片中系统地恢复出来的。”
HTC 实现的问题在于,PRNG(伪随机数)更新的操作是线性的,而不是随机的。所以他们没有得到一个预期的多项式,得到的是一个系数之间有线性相关的多项式。因此,攻击者只需要攻破两个被信任的联系人,就可以从他们的碎片中重构种子词。因此,只要两个被信任的联系人串通,就可以恢复出种子词。
此外,Ledger DonJon 发现 sss_update_secure_random_buffer
,也就是 PRNG 初始化函数,从未被调用过。因此,用来加密种子词的密钥总是相同的,而这个密钥会发送给每一个受信任的联络人,因此其中每一个人都可以解密种子词、转移资金,无需串谋。
相反,在比特币中,一个多签名脚本的构造是很直接的:
M <Public Key 1> <Public Key 2> … <Public Key N> N CHECKMULTISIG
它比私钥分割简单得多,实现者不太可能会创造出一种破坏系统安全性的错误。这里不需要使用随机数生成器,也不需要别的敏感的密码学操作;都是比特币协议的原生操作,是高度标准化的,而且得到了许多库的实现。
没有标准的实现
另一方面,Shamir 私钥分割也没有标准。这意味着,使用某一种软件产生的碎片,可能无法用在另一种软件。这可能会将用户锁定在一种实现中,从而损害用户的主动权。而且也提高了因为操作失误和实现 bug 而丢失资金的风险。广义来说,这是一种使用异构软硬件的障碍,会产生另一种单点错误。
值得一提的时,现在已经有一种分割比特币种子词的提议标准了,就是 SatoshiLabs Improvement Proposal 39。它已经开发了两年了,似乎设计得很巧妙,而且已经有了至少 4 种编程语言的实现。
社交恢复复杂性
我们常常将 SSS 视为实现私钥的 “社交恢复” 机制的办法。但是,SSS 构造给所有的碎片分配了同等的权重。而在现实中,信任在你的不同社交圈(朋友、商业伙伴、家人)中并不是均匀分布的。不同个体在一定的社交圈子内是无差别的,但并不总是如此。“家人” 跟 “商业伙伴” 是不同的。适应这种差别的唯一办法就是给同一个人多个碎片。
举个例子,设想一种情形,我们希望碎片在 2 位家人和 4 位朋友之间分散。我们希望下列组合就足以恢复出私钥:
- 2 位家人,0 位朋友
- 1 位家人,2 位朋友
- 光朋友不行
那么我们需要生成 12 个不同的碎片,阈值要设为 6。家人各拥有 4 个碎片,朋友各拥有 1 个碎片。
那么,一个人拿着多个碎片,负担是不是会变大呢?有待探讨。公平地说,多签名也同样有权重复杂性问题,但因为下面这个验证的问题,SSS 就变得毫无吸引力了。
糟糕的可审计性
假定一个私钥从碎片中恢复出来了,你不可能知道哪几个碎片参与了恢复。但是在链上多签名脚本中,每个签名密钥的 “身份” 都存储在区块链上,而且可以在私钥被盗之后用于事后分析。
无法验证碎片的完整性
如果你有一个碎片,据说它属于某个分割好的私钥,你没法验证其它碎片有没有损坏。你也不可能在恢复过程中校验各个碎片的正确性 —— 要么能恢复出来,要么不能。
这就意味着,碎片持有者可以自由提交假的碎片,从而阻止正确的私钥被重构出来。一个恶意的碎片持有者,如果具有充分的信息,甚至可以制作出另一个碎片,使得重构出来的私钥正好是他们提前选定的值。为了验证碎片的正确性,你必须使用一种叫做 “可验证的私钥分割” 的技术,这样你就能验证碎片持有者是诚实的了。
在一个多签名合约中,如果一个恶意的会签人给出了一个无效的签名,那将立即被揭穿。同样值得一提的是,SLIP–0039 通过为每个碎片加入校验和,解决了碎片完整性问题。
旁路攻击
在现实世界上,密码学应用常常存在旁路攻击的威胁,也就是攻击者尝试从应用的时序(timing)、缓存、报错,等等信息中提取有用的信息。许多现有的 SSS 实现都完全没有针对 EMI/DPA 旁路攻击强化过,许多实现对简单时序旁路攻击也没有强化过。精心设计的私钥生成软件和签名软件花了大量功夫来避免私钥泄露给能够观察你的时序和射频辐射(RF emissions)的观察者,但 SSS 软件通常都会泄露这些信息。具体来说,任何使用通用的 bignum 库实现的方案,最终都无法抵御时序旁路攻击。
使用多签名装置、使用异构软硬件来生成和管理在地理上分散的私钥,可以缓解这类攻击。
一无是处?
在一个更完整的系统中,SSS 依然有一些实用的用途,但它不能挑大梁。
举个例子,多签名装置的其中一个私钥的社交分割,是非常低风险的,SSS 实现的灾难性问题也不会造成灾难性的资金丢失。
Schnorr 门限签名也有可能跟分割的私钥结合:
- 你无需重新组合私钥,就能构造签名
- 你可以在分散的设备上生成私钥
但是,这样的系统中,SSS 的部分依然是难以实现的 —— 在上述方案中实现 SSS 的最佳方式,依然是个未决的问题。
另一个值得探索的有趣领域,是分层的 SSS,但依然需要更多的测试。
我们和继续研究这些可能性,因为这个领域一直在发展!但至少在目前,出于上述理由,我们强烈反对使用 Shamir 私钥分割。
(完)