在之前的一篇 文章 的 BTC 的签名流程细节里有提到一个随机数 k 。里面有提到说,如果两次签名使用了相同的 k 则会导致私钥泄漏,本文介绍更多的利用 k 破解私钥的特殊案例,案例的所有代码备份在 github 上了
攻击案例
相同的 k
- 单纯地用同一个私钥及同一个 k 签名至少两次,即导致私钥被计算得出。相关论文参见 这里(论文里的 nonce 即本文的 k) 。
- 两个不同的私钥用同一个 k1 签名了两次消息后,再用同一个 k2 再签名两次消息,那么通过这四个签名及四个消息也可以计算出这两个私钥
我们可以检索区块链(比如 eth)上所有的交易签名,只要发现其前 32 个字节(即 r)有相同的签名出现,那么必定说明这些签名用的同一个 k ,就可以结合交易内容计算出其私钥。 我觉得链上一定有这样的交易。
泄漏的 k
正常情况下 k 都是隐藏在签名过程中,一般不会暴露出来,如果一旦遭遇了某种特殊场景导致 k 泄漏,那么结合签名结果和签名的原文,私钥也同样可以被计算出来
这种场景下的攻击方式可以是供应链攻击
低安全性的 k
一个正常的安全系数为 256 bit 的签名算法,k 的取值范围大约在 0 和 2^256 - 1 之间,但如果 k 在 0 到 2^127 这个范围内(或更小)随机的话,那么就可以由两次签名结果及其中一个签名原文,计算出私钥来(LLL 算法)。
以此推广,只要 k 的前 n 位(或连续的任意 n 位,n 最低可以低至 4,当然相应地就需要收集更多的签名)固定,都可以将私钥计算出来。详情可以参考这篇 文章
这个也可以是供应链攻击,而且这个场景下可以做得比上一个更加隐蔽,不懂底层原理的开发者很容易就上当
故障攻击
这种场景比较少见,意思是说,签名过程中出现异常,在计算 k * G 的结果里某个 bit 位意外翻转了,导致最终生成的签名验签失败。此时一般会重新再尝试一次(重启大法)就能正常签名了,但失败的那次签名并不是毫无意义的,它可以结合下一次成功的签名最终计算出私钥。 当然这样的异常场景需要保证两次使用的 k 是一样的。
这个一般是硬件相关,使用人工手段迫使硬件产生一个故障签名,然后再结合正确的签名推算出硬件的私钥来
应对措施
- 遵循 RFC 6979 方案生成 k ,大部分区块链的 ECDSA 都已经是这样做了
- 拒绝使用 ECDSA ,改用 EdDSA
参考资料
https://asecuritysite.com/ecc/ecd
https://asecuritysite.com/ecc/ecd2
https://asecuritysite.com/ecc/ecd3
https://asecuritysite.com/ecc/ecd4
https://asecuritysite.com/ecc/ecd5
https://asecuritysite.com/ecc/ecd6
https://asecuritysite.com/ecc/ecd7