几个月前,小太写过一篇关于随机数
的文章(http://www.8btc.com/random23304),在业内引起了不少讨论。之后,比太发布了极随机(XRANDOM
)解决方案,让普通用户也能轻松便捷的生成真随机的比特币私钥,这都是我们在随机方面所做的努力。不过,曾有开发者问过这么一个问题“你们为什么在随机上下这么大功夫啊?有这必要吗?”,对于此类问题,小太只能简单的回答“非常有必要!”,因为随机是比特币安全的“命根子”,如果使用了不够安全的随机数,您的比特币就很难安全。
既然都用到了“命根子”这么严重的词语来描述“随机”对于比特币的重要性,当然是有原因的,本文中,小太将尽可能用通俗易懂的语言来给大家说说这个事(想更深入的了解椭圆曲线ECDSA
的原理及算法请查阅相关技术文档)。
我们都知道,比特币钱包解决方案应该使用密码学安全的随机数(甚至是真随机)来生成私钥,对于大额资产来说,甚至应考虑冷存储的方式来离线、断网的保管私钥,但即便这样还不够,因为签名也需要安全,签名交易时同样需要随机数,这个随机数的品质也会决定到私钥的安全。
签名一个比特币交易时,比特币钱包软件会新获取一个随机数k,同时用k来计算出一个r,然后用“私钥+k+r+交易hash
”来计算出签名Sig,最后将“公钥+r+Sig”广播出去,交易就成功发布到比特币网络了。
其它节点可通过检查“公钥+r+Sig
”来验证签名是否正确,就像从公钥不能反推出私钥一样,从r值也不能反推出k值,这看起来都很完美、毫无问题。 但如果k值“不够”随机,那又会怎样?
如果由于钱包软件所依赖的随机数生成器不安全、不可靠,导致签出的多笔交易之间发生了k值重复,则r值就会重复(r由k计算所得),也就相当于使用了相同的k、相同的r、相同的私钥签名了不同的交易,这样,对于外部的旁观者(黑客)就能够直接通过“公钥+r+Sig1+Sig2
”来反推出私钥。然后呢?比特币资产当然就会被盗,这种盗窃并不需要木马、病毒和钓鱼邮件,无论您的私钥随机性如何、是冷存储还是热存储,都没用,分分钟您的比特币就会被转走,可怕吧?(有兴趣的童靴不妨找些有问题的地址转几毫试试,能被哪个黑客偷走,那就得看谁的偷币程序跑得快了)
看到这里,大家就该明白,k值这个随机数与私钥一样重要,k值不安全的话,私钥再安全也没用。现在知道为什么小太会说“随机是比特币的命根子”了吧?
本文中的主要内容参考了Filippo Valsorda
的文章,Filippo甚至提到了bitcoin-core/qt也是不够安全的(2013年的Pull-Request改进至今仍未被合并进主干),当然也包括了blockchain.info(因为依赖于浏览器的随机数解决方案)。 之前,我们团队也给blockchain.info提交过Pull-Request,建议他们停止支持无法提供安全随机数的浏览器(如:旧版IE等,因为在这类浏览器中,会使用Math.Random来生成随机数),可惜,blockchain.info团队之后关闭了我们的Pull-Request,关闭的理由与他们答复Filippo的一样:
1、blockchain.info还会使用服务器端随机数(
Server Entropy
); 2、blockchain.info还会采集键盘/鼠标的点击事件;
这个答复其实还是有问题,我们在blockchain.info关闭Pull-Request
后,给出了进一步解释:
1、服务器端随机数其实是个更差的解决方案,因为服务器管理员就具备了使用该随机数“作恶”的可能性,blockchain.info还需要证明自己没保存该随机数、证明该随机数是密码学安全的,这反而引入了更多的问题; 2、blockchain.info所谓的采集键盘/鼠标的点击事件,貌似是增加了随机性(按照他们自己的说法),其实不然。因为人们在该网站上操作时,所做的键盘/鼠标操作是非常有限且很有规律的,显然无法使用这些输入及位置等信息来作为增加熵池“噪声”的随机源,他们应该也很清楚这一点,所以他们使用的其实是键盘/鼠标的点击时间(而不是事件),这与BitAddress.org的熵采集方式完全不同,使用时间作为随机数的种子显然是不安全的;
除了上述两点之外,这种实现方式在k值的随机性方面还有更为严重的问题,那是因为blockchain.info会在页面加载时获取一次服务器随机数,之后就会一直使用这个随机数(除非重新加载页面),配合上那密码学都不安全的Math.Random,再加上所谓的键盘/鼠标点击时间,多转个几笔帐,出现重复r值的概率就会大大提升,这个问题显然很严重,理应引起开发者的重视。历史上真正因r值重复所导致的丢币其实也大多都是发生在blockchain.info的,很奇怪他们为什么会如此坚持的兼容旧版IE等无法提供安全随机数的浏览器,并一直使用这种有很大风险的解决方案;
好了,现在大家总该明白随机数对于比特币的重要性了吧?既然k值这个随机数对于比特币安全如此之重要,钱包开发者和比特币企业应该如何做呢?
1、应尽可能的使用密码学安全的随机数生成器来生成k值,在这一点上,如同上一篇文章中所提到的,从安全级别的角度上讲:内核态(
/dev/urandom
)优于应用态(各种高级语言或库中自行实现的SecureRandom),更优于各种浏览器中自行封装的SecureRandom解决方案(浏览器提供密码学安全的随机数是由谷歌在2011年发起的,并未久经考验,并且浏览器厂商又太多,各自的实现方式也不一定足够安全); 2、还可以使用RFC6979规范
来生成k值; 除了做到上述两点之外,比太钱包从v1.2.0版开始还提供了一个全新的“RCheck
”功能(虽然比太钱包在签名交易时使用的是密码学安全的/dev/urandom及RFC6979规范来生成的k值,但我们仍然认为,增加r值检查,确保不会签出r值重复的交易,即便会增加一点点性能开销,还是值得的)。
最后小太再卖个关子,仅仅r值不重复还不够,比特币里的“坑”远比想象中的要多哈,下一篇文章将进一步讨论随机问题,敬请期待!
作者:比太钱包 官方微博:@比太钱包 http://weibo.com/bither 捐赠地址:1BsTwoMaX3aYx9Nc8GdgHZzzAGmG669bC3