在加密货币世界中,“Not your keys, not your coins”的观念深入人心。但权力越大,责任也越大。没有中心化的服务提供商,用户需要自己管理密钥的整个生命周期,包括密钥的生成、分发、存储、使用、更新、作废以及恢复。对小白来说可谓战战兢兢,时刻面临密钥丢失造成的资产或控制权丢失风险。这极大的损害了用户体验,影响区块链的推广和普及。
如何设计安全的密钥管理系统,实现足够的冗余,提供可靠的密钥恢复能力,是区块链行业面临的一大挑战。本文介绍一种基于智能合约的社交密钥恢复功能,它的核心思想来自social-recovery-using-address-book-merkle-proofs,并且在EIP2429中得以标准化。
社交密钥恢复
社交密钥恢复的思想由来已久。
在传统的web应用中,中心化的服务提供商会提供一系列的密钥恢复途径,比如:
1. 用户可以预留经过验证的手机号,邮箱,密码丢失时通过它们重设密码;
2. 在微信这类IM应用中,还可以提前设置几个亲密的联系人,用于在账户被冻结情况下的协助验证解封。
这些做法本质上都是提供了一个冗余的多因子身份认证。
在区块链上,用户的身份往往由公钥代表,他的所有数据都和这个身份绑定,这些数据可能包括:1. 账户余额;2. DAO里面的角色和权限,等等。私钥一旦丢失,可以认为在当下的计算能力下是无法再恢复的。但可以设置一个新公钥,绑定到丢失公钥的数据上,或者是代理丢失公钥的所有操作权限。这只能由智能合约来实现。
一个简单的方案可以是这样的:
Alice持有一对密钥 < secret_a, public_a> ,她的账户public_a里面转入了100个token。她为了防止密钥丢失,将Bob,Charlie, Dave作为恢复密钥的委托人,三者的公钥分别为{public_b, public_c, public_d}。Alice还设置了一个域值,任意两个人协助认证都可以重新设置公钥。这样就是一个最简单的2/3链上门限密钥恢复方案。
有天Alice真的丢失了自己的密钥,她重新生成了一对密钥< secret_a_1, public_a_1> ,找Bob和Charlie帮助,二者分别使用各自的密钥调用合约的恢复密钥接口,提交两笔交易tx1 = {(public_a, public_a_1, public_b), signature_of_bob}, tx2 = {(public_a, public_a_1, public_c), signature_of_charlie}。这样合约验证后通过了门限检查,就可以将public_a的账户所有人替换为public_a_1,Alice成功找回了这笔资产啦!
隐私保护?
上面的方案存在几个可能被攻击的方面:
1. Bob, Charlie, Dave三人看到自己作为恢复功能的委托人,可能会共谋私自替换掉密钥,夺走这笔资产;
2. 假设Bob, Charlie, Dave都是不作恶的好人,但由于他们的公钥在区块链是公开可查询的,攻击者可能选择攻击这三人中的薄弱环节,直到获取足够的私钥;
3. 攻击者在知道Bob, Charlie, Dave身份的情况下,还可能模仿Alice的身份(使用AI模仿声音、视频已经很成熟!),向他们发起恢复密钥的请求;
为了避免这些攻击,不同的方法可以被采用,比如所有的密钥恢复过程存在一个锁定期(lock period),在锁定期内Alice发现有人恶意重置公钥,在她私钥没有丢失的情况下,可以主动终止密钥恢复过程。当然这需要一些通知机制的配合。也可以再增加一些经济上的惩罚措施,任何企图恢复密钥的人需要提前存一笔指定额度的资金,正常流程下在结束后会返还,但作恶情况下会被slash。
但如果仔细思考,很容易意识到,这里列举的攻击方法都由于Alice的协助恢复委托人账户在链上是公开可见的,缺乏隐私性。如果委托人的身份在链上是“加密”的,但同样能执行后续的验证过程,是否就能很好的降低风险呢?
协助验证的交易{(public_a, public_a_1, public_x), signature_of_x},本质上是需要证明该交易的签名者是委托人集合的一份子,也就是成员证明,membership proof。但由于保护隐私的要求,我们在链上又不能存储成员的明文。
默克尔证明
一种可行的方案是使用默克尔树,这个区块链里面频繁使用的数据结构,它不仅能够提供防篡改的功能,也能作为成员证明的工具。
比如,上图中的绿色叶节点是树的一部分,提供其中三个黄色的节点哈希,形成一条到根节点路径,即一个合法的默克尔证明(merkle proof)。
其他方案,甚至是具有零知识性质的成员证明工具也可以作为思考的一个方向。
概念实现(PoC)
基于上面的讨论,这里给出一个保护隐私的链上密钥恢复方案:
1. Alice本地存储了Bob, Charlie, Dave等人的公钥薄,以这些公钥作为叶节点,生成一颗默克尔树,她将根哈希(root hash)提交到合约里,同时设置恢复阈值等参数;
2. 在密钥恢复过程中,Alice寻求Bob, Charlie的帮助,Bob对Alice的新公钥进行签名,Alice也生成了一个Bob的默克尔证明,提交交易tx1 = {public_a, public_a_1, public_b, signature_b, proof_of_bob_membership},合约验证交易签名合法性和证明有效性,并且记录下bob的公钥,类似重复,直到满足阈值。
这个方案较好的解决了前面的攻击可能:
1. Bob可能知道自己是委托人,但他并不知道其他委托人,则无法形成共谋;
2. 链上只有一个root hash,不会泄漏委托人的身份,则不会被定向攻击;
3. 攻击者在未获得Alice本地的地址薄时也就无法伪造身份发送恢复请求;
我基于substrate的social-recovery组件,开发了一个PoC,增加了隐私保护功能,代码参考:https://github.com/bitrocks/secret-social-recovery.