区块链里的 keyless 方案

介绍

keyless 并非不存在私钥的意思,而是指没有任何人/事物可以知道私钥或者很难拿到私钥。之所以大家会去研究这个,主要也是因为区块链火之后引发了更多的私钥被盗/泄漏的安全事件。

想减少这些悲剧的发生,有两种解决思路,一个是思考如何更加安全地保管私钥,另一个就是干脆绕过问题,想一个不用保管私钥的方案出来。

方案

智能合约

以 ETH 为例,创建合约前可以提前计算出合约地址。我们可以利用这个特性构造一个 keyless 的方案出来:

在 ETH 上创建合约需要用到 create 或者 create2,前者创建出的合约地址取决于 sender 和其 nonce,而后者则取决于 sender、salt 和 code。所以无论是 create 还是 create2 都是可以提前就计算出未来的合约地址的。

  1. 编写合约代码,里面有一个只有 ownerAddr 才能调用的 withdraw 函数里面接受一个参数: beneficiaryAddr 即把合约里的资产全部转到该地址
  2. 选定一个会用于部署未来合约的地址 deployAddr ,假定一个为 n (n > deployAddr.nonce)的 nonce,然后计算出一个未来的合约地址 contractAddr (假设我们用 create 来创建合约)
  3. 将你想要保管的资产转到这个 contractAddr 里,此时这个 contractAddr 还只是一个 EOA 地址。在外人看来你只是将资产从一个 EOA 地址转移到了另一个 EOA 地址,但他们不知道的是连你都没有这个地址的私钥,当然也就不可能出现私钥被盗/泄漏的问题了

某天当你想提取资产的时候,就把 deployAddr 的 nonce 值刷到 n-1 ,然后再把第 1 步里的合约部署到链上并设定好 ownerAddr,那么前面的 contractAddr 就会变成一个真正的合约地址了,最后你只需要用 ownerAddr 调用一次合约里的 withdraw 函数并指定一个 beneficiaryAddr 就行了,之前存的所有资产就都转移到 beneficiaryAddr 上了。

  • 在提取资产前注意不要频繁使用 deployAddr 避免将其 nonce 刷得超过了 n,所以 deployAddr 的私钥也需要适当保管一下,如果泄漏且对方知道你的意图,他可能会故意刷 nonce 做出损人不利己的行为导致资产永久锁死
  • ownerAddr 的私钥也需要适当保管一下,当且仅当坏人知道你的意图和合约源码和拿到 ownerAddr 的私钥时,你的资产会被盗走

一次性存取

这个方案理论上适用于所有采用 ECDSA 验签的区块链,但它只支持一次性的存和取(严格来讲这里的一次性存是指不能存太多超过指定金额的资产)。还是以 ETH 举例:

  1. 先提前构造一笔未来的取款交易,在 ETH 上需要 6 个字段:nonce/gasPrice/gasLimit/to/value/data 。其中 nonce 设定为 0,gasPricegasLimit 就设置为最近网络上的一个均值或者方便起见更高一点都行,to 设为 beneficiaryAddr ,value 就是存的 ETH 的数量,假定为 100 个 ETH 吧,最后的 data 可以为空。最后将这些字段按照 RLP 编码后的结果做一次 keccak256 得到 preimage
  2. 随机生成一个 65 字节的 ETH 的签名结果 signature (即 r、s、v),但 v 只能从 0x1b0x1c 中选取(原因在这篇 文章 里有提到)
  3. 将 preimage 和 signature 通过 ecrecover 还原出一个公钥 pubkey,然后对其做一次 keccak256 取后 20 字节得到一个 ETH 地址: depositAddr
  4. 给 depositAddr 转 100.01 个 ETH,这笔交易就是存款交易了,多出来的 0.01 是用来作为提款交易的 gas 费的

这样哪天你想取钱了,那么就把前面构造好的交易连带生成的签名 signature 一起发送到链上,beneficiaryAddr 的余额就会多出 100 ETH 了。

  • signature 需要保管好,但是这个丢了其实也没啥,没人知道这是个签名,即便知道了也不知道怎么使用,即便知道了怎么使用也只是提前帮你做了取款操作,因为这笔资产只能提款到 beneficiaryAddr ,而他还不知道 beneficiaryAddr 的私钥
  • beneficiaryAddr 的私钥需要保管好,但即便泄漏了也没啥,因为除了你没人会知道这个地址上未来的某一天会多出 100 ETH,在这之前其余额会一直是 0(当然也可以结合上面的智能合约的 keyless 方案将 beneficiaryAddr 作为 contractAddr,这样连 beneficiaryAddr 的私钥也都不需要保管了,不过这个感觉就搞复杂了……)
  • 取款用的 0.01 ETH 不够 gas 费的话也不慌,可以继续往里面再转点钱直到足够

Passkey/WebAuthn

这个是各大 web2 厂商近些年一直在推进的 keyless 方案,其实就是用的设备(电脑、手机等)独有的加密芯片结合用户的生物特征(指纹、面容、虹膜等)来取代当前的用户名、密码的登录验证体系。

那 web2 的 Passkey 和 web3 能结合吗,能,但不多……主要是因为目前 Passkey 支持的验签算法和区块链用的验签算法交集比较少,目前能用的是 secp256r1 也只能在支持密码学原语的 CKB 上使用,比如 .bit 就已经支持了 Passkey ,体验很好,用户不用去关心密码的保管问题了

对,这是硬广。不过之所以不写太多关于 Passkey 的事,主要是这个东西内容比较多也比较复杂,其实网上资料也不少,自己搜吧