主页 > imtoken安卓官方下载 > 加密钱包签名nonce随机值暴露私钥风险@Aaron Chen

加密钱包签名nonce随机值暴露私钥风险@Aaron Chen

imtoken安卓官方下载 2023-02-05 06:16:24

作为区块链安全领域的从业者,我一直对密码学和安全有着浓厚的兴趣。 上周我读了一篇非常好的文章。 与作者联系征得同意后,我将其翻译成中文。 和朋友分享。 以下为原文:

上周,以太坊黑暗森林中的一个怪物终于向我露出了獠牙。 和tux沟通后得知了这只怪物的存在,于是投下“我的诱饵”,果然16小时后它上钩了。 看到自己在Etherscan上消失的ETH,心有余悸。

这个怪物一直在观察以太坊网络,试图找出以太坊交易中那个未被注意到的错误:签名中 k 值的复用 。 为了找到这个怪物,我引诱它存在。 为了理解我是如何做到的,需要先介绍一些关于椭圆密码曲线签名算法(ECDSA)和数字签名的知识。

ECDSA

椭圆曲线数字签名算法 (ECDSA) 是支撑当今两个最大区块链比特币和以太坊的加密基础。 就像现实中人们用自己的签名来证明自己的权益一样,基于ECDSA的数字签名可以证明这些区块链上数字资产的所有权。 数字签名可以提供以下两个方面的证明:

私钥的所有权。 每个私钥都绑定了一个公钥,这个公钥也用来在区块链上生成一个“地址”。 使用私钥签署消息。 在区块链的上下文中,这条消息就是交易。

ECDSA之所以有效,是因为我们可以很容易的用私钥得到公钥,但是从公钥推到私钥是不可能(或困难)的。 但是,如果获得数字签名,则在一定条件下可以推导出私钥。 下面我们来谈谈一些技术细节。

要完成消息的签名,我们需要使用私钥 d、随机值 k 和消息的哈希值 h。 同时,在椭圆曲线中加入G和n,通过下式计算得到该消息的数字签名r和s。

\[r = k * G\ mod\ n \\ s = \frac{h+d*r}{k}\ mod\ n\\\]r和s是这条消息的数字签名。

什么是随机数?

在椭圆加密算法中,计算数字签名的随机k值是非常关键的。 k值永远不能暴露,k值永远只能使用一次。 这就是随机 k 值被称为 nonce 的原因。 下面还将使用 nonce 来指代 k 值。 如果黑客知道某个签名对应的k值,那么他就可以通过下面的公式计算出这个签名的私钥。

\[d = (s*k - h) * r^{-1}\ mod\ n \\\] 同理,如果nonce在两个不同的签名中被复用,这个nonce(k值)也可以通过计算得到下面的公式

\[k = (h_1 - h_2) * (s_1 - s_2)^{-1}\ mod\ n \\\] 有了nonce,私钥就可以通过上面的公式计算出来了。

那么我们在实践中如何判断是否存在nonce复用呢?

回想一下,ECDSA数字签名中r的计算过程是r = k * G mod n。 在这个公式中,G和n都是确定的,唯一的变量是k,所以如果用同一个私钥对两个不同的消息进行签名,得到的签名中r是相同的,所以可以判断有一个随机数重用问题。

了解了这些之后,我们再来看看以太坊上的交易。 如果一个地址下两个交易的签名r值相同,s值不同,则可以判断该地址下存在nonce重用。 这就是“怪物”的意义所在。 也是我下面用的实验方法。

注意:对于普通用户,只要选择靠谱的钱包,不关心nonce泄露和复用。 但对于区块链开发者来说,这些都是需要了解和关注的。

放上诱饵以太坊私钥怎么用,引蛇出洞

为了引蛇出洞,找到这个怪物,我构造了两笔交易。 这两个交易的签名使用相同的 nonce,因此它们的签名将具有相同的 r 值。 我在这个地址上留下了一些ETH,所以如果有人盯着这个地址以太坊私钥怎么用,那么它可以通过签名推导出私钥来转移这个地址上的ETH。

为了制作这些诱饵,我不得不构造两个 nonce 重用交易,当然你不能为此目的使用 Metamask。 所以我为此使用了 ether.js。 看看下面的示例代码。

1
2
3
4
5
6
7

var drbg = new HmacDRBG({
      hash: this.hash,
      entropy: bkey,
      nonce: 1, // changed from "nonce"
      pers: options.pers,
      persEnc: options.persEnc || 'utf8',
    });

(代码仅供参考,请勿在自己的钱包上尝试)

这里我设置了nonce为1!然后我重新生成了一个私钥,并向该地址收取了0.04ETH,然后我使用下面的脚本生成了两笔交易

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

const ethers = require("ethers")
  
    async function main(){
      const privateKey = "not-leaking-it-this-way"
      
      let wallet = new ethers.Wallet(privateKey)
      console.log("Using wallet:", wallet.address)
      
      const provider = new ethers.providers.JsonRpcProvider("rpc-endpoint")
      let signer = wallet.connect(provider)
      
      const tx = await signer.sendTransaction({
          to: wallet.address,
          value: ethers.utils.parseEther("0").toString(),
          gasLimit: 45000,
      })
  
      console.log(tx)
  }
  
  main()

由于我将nonce设置为1,所以这两个交易的签名具有相同的r值和不同的s值。 我写了一个简单的脚本来验证它:

1
2
3
4
5
6
7
8

robert@mbp nonce-reuse-bait % node get_r_s.js
  Transaction 1
  r: 0x340709f674d030dda4aa8794bffb578030870bdfe583e7f41aea136a7ca1ed94
  s: 0x3e5b92d8c5a2a033c9e5eb53ff2946681b28ab82fa5b17d0a9c48d139e78fe2c
  
  Transaction 2
  r: 0x340709f674d030dda4aa8794bffb578030870bdfe583e7f41aea136a7ca1ed94
  s: 0x2c17cf960d1af7602f875afc56d01eddcc67bb03e962877444ed8d4c1287ab7a

随着这两笔交易的上链,签名私钥其实已经暴露了。 我已经下好了饵,现在就静静地等待怪物上钩。

一眼望去,怪物出没

发送完这两笔交易后,我不断刷新区块浏览器,看地址上的ETH是否还存在。 但奇怪的事情,一切照旧。 又是几个小时过去了,一切依旧如常。 我开始怀疑自己是不是看错了,但是时间不早了,我先去休息了。

第二天一早,我检查了我的帐户,它出现了! 我地址上的ETH被拿的干干净净,我故意留在地址上的0.04 ETH被拿走了!

用手提挖以太坊_以太坊私钥怎么用_sitehqz.com 以太坊和以太坊贸易的关系

怪物终于露出了獠牙,他看着以太坊上的交易,看看有没有用相同 r 值签名的交易。 当他知道后,他会计算他们的私钥,并取出他们账户中的所有资产。 我有点困惑的是为什么它花了这么长时间才出来? 有可能它一直在默默等待,看是否有更多的资产从这个账户转入,以便一下子全部转入。

仔细看看这个怪物,不仅我是唯一的受害者,它还抢走了别人的资产!

用手提挖以太坊_以太坊私钥怎么用_sitehqz.com 以太坊和以太坊贸易的关系

该地址一直有从不同地址转入的 ETH。 到目前为止,该帐户中的资产价值为 3,700 美元。 为了验证这些被盗地址是否也存在nonce重用问题,我写了一个脚本来检查它们的历史交易检查是否也有相同的r值。 我查看了这个怪物(黑客)窃取的第二个地址,发现它之前发送的交易也存在r值重用的问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14

robert@mbp nonce-reuse-bait % node get-tx-history.js
  
  Same r found!
  r: 0xf0d7b10f398357f7d140ff2be1bea9165d32238360ad0f82911235868be7c6e1
  hash: 0xb5d2454d7380bfa7ac75ec76f15eecb56e60941429153081fe799fb53a7ff901
  r: 0xf0d7b10f398357f7d140ff2be1bea9165d32238360ad0f82911235868be7c6e1
  hash: 0x9e459be7fa9950835a3c2594d3440c684fed05fa8e12e8088cc7776c4afb364c
  
  
  Same r found!
  r: 0x41d43fd626c24e449ac54257eeff271edb438bbabbc9bee3d60a5bd78dc39d6d
  hash: 0x670f66ff71882ae35436cd399adf57805745177b465fdb44a60b31b7c32e4d16
  r: 0x41d43fd626c24e449ac54257eeff271edb438bbabbc9bee3d60a5bd78dc39d6d
  hash: 0x374180005946ef3b1906ee1677f85fa62eb5a834aa0241b4c9c74174bca26a07

这进一步证明了我的猜测,这个怪物密切关注以太坊上的交易,时刻关注是否有nonce重用。 一旦发现重用nonce,它就会慢慢悄悄地拿走这些地址上的所有资产。

我分析了其他被这个怪物攻击过的地址后,惊讶地发现有些地址并没有nonce重用的问题。 事实上,在它攻击的20个地址中,有9个存在nonce重用问题,而其他的则完全没有。

那么为什么其他 11 个地址会受到攻击呢? 老实说,我不知道。 一种可能是这个怪物还有其他的策略来尝试推导出私钥,比如使用一些常用的单词和数字来推导出私钥。 还有一些其他方法可以利用 nonce 生成中的漏洞。 但是我没有找到这个怪物行为的证据。

我终于在以太坊的黑暗森林中找到了这个怪物,但是它的下一个受害者会不会有或者会是谁呢? 这仍然是一个谜。

亚伦的简短评论:

私钥的生成不是随机的,nonce的生成也不是随机的,重复使用nonce会有泄露的可能。

对于用户来说,选择靠谱的钱包是避免这个问题的最好办法,尤其是推荐带有真随机数发生器的硬件钱包。

对于开发者来说,重视随机数的产生,选择靠谱的密码库。 同样使用确定性 ECDSA 签名算法是避免此问题的另一种方法。