零知识身份:我们该如何实现它?
来源 | 0xparc.org
作者 | gubsheep
翻译 | 双花 (@doublespending)
校对 | ECN
推荐阅读:《》
这是解释为什么密码学的新进展对数字身份原语很重要的系列文章。第一篇文章解释了“为什么”;本文会解释“怎么做”。
在上篇文章,我们讨论了为什么新的加密工具(如 zkSNARKs )对构建下一代数字身份基础设施至关重要。在本文中,我们将深入了解构建基于 ZK 的身份系统原型要哪些技术?
ZK 身份项目的规模比任何单个组织都要大得多。设定标准、构建基础设施、迭代身份原语和应用设计的问题将逐渐浮现,而且需要不同利益相关方和各领域专家的贡献。
鉴于 ZK 身份机制对许多人有着潜在影响并严重依赖公共物品(public goods)—— 开源基础设施、工具、标准和协作,由一个有机、社区驱动的“生态系统”自下而上推动而非由一个公司自上而下推动尤为重要;同时,需要特别关注发展的可持续性及激励的设计。
单一公司在这样一个动态的生态中取得成功的可能性也越来越小,因为技术栈中在各层次上独立发展的技术正在快速地变化。相反,我们需要培养和协作出一个由模块式、敏捷,半独立,拥有相同愿景的团队组成的生态系统。
0xPARC ZK 身份工作组致力于通用 ZKID ,并邀请积极思考此问题的其他人加入我们,相互切磋。
ZK 身份的组件
ZK 身份工具必须使得数字系统中的参与者能够对身份和信誉进行声明。具体来说,这些声明可以归结为有关零知识领域的密码运算(如签名验证、密钥生成、哈希和加密)执行的数学命题。我们可以将这些“组件”组合在一起,为更复杂的声明构建 ZKP :一个简单示例可以参阅我们的文章《ZK 群签名》(https://0xparc.org/blog/zk-group-sigs)。
一些运算和密码方案使用 zkSNARKs 实现会比其他技术更加高效。从长远来看,SNARK 友好的密码标准将会被目前尚未存在的新型身份提供商所采用。
例如,使用 SNARK 友好密码体系的公私钥签名方案的区块链(或更具表现力的系统,如[账户抽象](https://eips.ethereum.org/EIPS/eip-2938))。
但为了原型验证并在短期发挥作用,我们的工具需要与现有的加密身份系统进行干净的集成 —— 例如以太坊目前的 ECDSA 签名方案,或在其他场景下与配对友好型椭圆曲线相结合的新近加密标准。
我们认为为 ZK 身份应用构建实用工具栈需要在四个方面取得重大进展:ZK 应用的设计模式、加密原语的 ZK 电路实现、电路安全工具以及开发者工具和基础设施。我们将在后文分别概述这四个方面。
ZK 应用和设计模式
首先,我们的工作成果应该触及终端用户,并使得有影响力的生产级应用成为可能。在开发 ZK 工具和组件的同时,我们必须找出使用和组合它们的最佳方法。以下是几个开放性问题:
• 身份的正确抽象到底是什么?
它是以太坊地址、以太坊地址集合、多签、智能合约钱包、ENS 名称、不同加密方案中的密钥对、秘密生物特征、一组证明、还是[高层级构造](https://eips.ethereum.org/EIPS/eip-2938)或者是一些完全不同的东西?
•人们所关心的作出的通用身份声明是什么?
上述的所有工作为我们提供了一种作出关于身份的可信声明的语言;现在,我们必须学会如何用这种语言进行表述。例如,直接引用和操作链上哈希数据的声明是否有意义,或者若大多数声明只是由半可信第三方作出的有效认证的简单证明,是否会更简单?在 ZK 证明中作出引用以太坊历史状态的声明是否可行?如果可行,证明工具应该能便捷地获取哪类历史声明?
• 未来 ZK 钱包和身份提供商应该提供哪些工具以及遵循哪些标准?
例如, Metamask 或硬件钱包目前支持用私钥进行数字签名,一般建议用户不要直接操作私钥。然而,在 SNARK 中进行 ECDSA 签名验证要比公钥生成昂贵得多 —— 这意味着希望对其 ETH 地址进行 ZK 证明的用户必须在慢得多的证明时间(通过有效签名认证的证明的方式)和较低的安全性(通过将其私钥明文从钱包复制到 ZK 证明生成程序上的方式)之间进行选择。
如果钱包软件最终为 ZK 证明的生成提供原生支持,那么这个问题将会被部分解决,我们目前正与 Metamask Snaps 团队进行试验。
• 支持 ZK 身份的应用需要遵循哪些接口呢?
链上和链下的应用程序在设计时应该考虑到 ZK 身份系统。例如,有意使用 ZK 身份系统的 NFT 封闭社区可能会在 NFT 智能合约中存储 token 的密码累加器及 token 所有者数据,以便用户更容易生成关于社区成员身份的声明。有意提供成员证明的群组标准需要被制定出来。
• 谁(或什么设备和环境)能够生成 ZK 证明?
落地应用生成证明的位置差异很大,取决于特定 ZK 证明是否能够在硬件钱包、移动设备、浏览器、消费者台式计算机上生成,或者仅能在专用证明服务器上生成。
解答这些问题的最好办法就是启动开发!我们希望一个强大的开发者社区在未来几年内开始形成,并用各种不同的方法来开发应用。
实际上,我们现今能够利用现有基础设施来开发几个 ZK 身份应用。这是一项高附加值的工作,除了交付有意义的应用外,这些项目还将为工具和基础设施的开发提供信息指引。
以下是一些 ZK 身份的初步生产级应用候选项:
• 隐私空投。许多 DeFi 应用的常见策略是在链上发布用户地址的 Merkle 根,并允许用户用发布在树上的地址调用一个函数来申请空投。Stealthdrop(https://github.com/nalinbhardwaj/stealthdrop) 是 ETHUni 和 0xPARC 社区的一个项目,该项目为其添加了一个隐私层,用户只需证明拥有与树中 ETH 地址相对应的私钥即可申请空投(用空值检查以防止重复申请)。
• 链上快照投票聚合。现今,许多 “DAO” 完全使用链下投票机制 —— 用户对投票签名并将这些签名发送到中心化服务提供商(即快照)进行统计。这带来了两个问题:
第一,投票可能会被中心化机构审查,前面的投票数据可能会丢失或者变得不可用/不可审计。
第二,投票是公开进行的,使得投票系统容易受到共谋攻击。我们可以使用 ZK 构造进行签名验证,将投票结果“卷叠”为投票者签名的单个 ZK 证明,该证明可以无信任地生成并在链上提交。部分分散中心化机构权力的适中构造 (即将权力分配给更广泛的证明人网络,他们去发布代币持有者快照的 Merkle 根) 也是可以换取可扩展性的。
• 匿名但可信的证明。这些身份工具可以用于证明你群组成员的身份,而无需透露确切你的身份。
例如,证明你对黑暗森林星球的所有权,并在不透露你身份的情况下加入 NFT 封闭社区;匿名但以大 V 的身份发布帖子,在不透露你账户的情况下证明你在推特上至少有 100 多万粉丝;或者证明你是一个立法机构的成员,并在即将到来的投票中匿名附议。里面某些应用在我们的文章《ZK 群签名》中得到更为详细的讨论。
ZK密码身份原语的 ZK 电路
技术栈中更进一步,我们需要核心密码原语的 ZK 电路及背后数学运算的高效,经审计的实现。下面是一些关键操作的示例,粗略地按依赖关系进行排序。
• 非原生的有限域算法:zkSNARK 算法基于素数域。例如,snarkjs 的所有值默认模一个 254 位的素数(BabyJubJub prime(https://iden3-docs.readthedocs.io/en/latest/iden3_repos/research/publications/zkproof-standards-workshop-2/baby-jubjub/baby-jubjub.html))。
然而,密码操作要求我们对潜在的更大数字进行操作——例如,secp256k1 操作要求我们获取两个 256 位数字模第三个 256 位数字结果的乘积。这些电路中涉及的最为昂贵的操作是范围检查,而一个约束优化的策略是更加小心地确定何时我们确实需要进行范围检查。
• SNARK 友好的哈希函数:哈希函数在用户必须作出密码承诺的应用中很有用。在哈希函数无需与现有标准集成的方案中,我们可以选择设计和实现专门为 SNARK 证明而设的高效哈希函数。这样的两个函数包括 MiMC(https://byt3bit.github.io/primesym/mimc/) 和 Poseidon(https://www.poseidon-hash.info/)。
• SNARK 不友好但标准化的哈希函数:在许多应用中,为了兼容现有的系统,我们必须使用 SNARK 不友好的哈希函数(如 SHA256 或 keccak )。
例如,为了证明私钥与 ETH 地址相对应,我们需要一个 keccak(https://github.com/vocdoni/keccak256-circom) 的 ZK 电路实现。我们还要下很大功夫来实现,优化和审计这些哈希函数。
• 椭圆曲线点加:椭圆曲线密码学是建立在椭圆曲线群之上的;因此,我们必须为椭圆曲线群定律(点加)构建 ZK 实现。这些运算是昂贵的,而且是 ZK 身份系统的一个性能瓶颈;巧妙地运用 PLONK 以及更好地实现大数算法可能会有助于性能提升。
• ECDSA 密钥生成和签名验证:用于 ECDSA 密钥生成和签名检验的 ZK 电路的实现将允许我们构建一种与现有基于 ECDSA 的身份系统(如以太坊)兼容的身份声明语言。构建这些原语需要我们高效地结合椭圆曲线点加和哈希函数的实现(https://github.com/0xPARC/circom-secp256k1/blob/master/circuits/eth_addr.circom)。
• 椭圆曲线配对:配对友好的椭圆曲线使我们能够使用双线性映射 —— 支持多项式承诺、BLS 聚合签名验证、递归 SNARK 验证、Verkle 树等。椭圆曲线配对 ZK 电路的高效实现将解锁大量新型密码运算。
• 密码累加器嵌入检查:一旦我们有用于多项式承诺验证的 ZK 电路,Verkle 树嵌入证明就可以在 SNARK 中验证。Merkle 树嵌入证明目前是可验证的,并适用于使用 SNARK 友好哈希函数构建的树。MPT 嵌入证明使我们能够在 SNARK 中验证轻客户端证明。
在所有这些场景下,验证累加器嵌入证明使得 SNARK 能够访问被卷叠为一个简洁全局状态承诺(根)的数据——如果您将要访问的数据、系统状态根,以及嵌入证明作为 SNARK 的输入,那么验证者只需验证你的简洁证明并检查根是否正确,而无需检查整个系统状态。
• 递归 SNARK 验证:在 zkSNARK 内实现椭圆曲线配对和/或多项式承诺验证,令递归 SNARK 成为可能。这开拓了身份声明中可编程性和复杂性的一个全新领域。
所有的这些电路将先由 R1CS 编写(近期考虑 groth16 证明系统),并在不久的将来优化为基于 PLONK 的证明系统。
开发者工具和基础设施
ZK 电路开发工具是一个重要的课题。目前,ZK 开发者需要相对较高的数学背景及技术经验,他们必须在相对底层的开发环境进行编码,并依赖手工或特定脚本来管理文件以及在开发流水线(设计->产品)上传递电路。此外,现有的开发者工具分散在多个研发团队、rollup 企业等组织中。
这里需要特别注意的是,一个健壮的工具栈对于 PLONK 而言尤为重要。PLONK 去除了对每个电路进行可信初始化的需要,大大加快了某些电路编译和证明生成的过程(归功于自定义约束),并为递归 SNARK 验证铺平了道路。
然而,相比 Groth16 ,PLONK 工具目前仍处于开发的初期阶段,其验证者优化程度偏低,高级协议特性尚未在一些系统中实现。此外,在 IR 标准和自定制约束语言设计上仍有很多工作要做。AZTEC、Electric Coin Co、ZK-Garage 等小组正努力地开发这些工具。
除了 PLONK 工具链外,下面是ZK开发者工具中的一些活跃领域:
• 更高层级的 DSL。当前用于编写 SNARK 的语言非常底层,需要开发者手工编写约束。我们对更容易开发,甚至集成了约束优化的生产级高层级 DSL (一个来自 0xPARC 社区成员的原型)感兴趣。
另外,值得具备的功能包括用户定义数据类型/注解以及更好的见证生成系统。更进一步,支持电路测试、验证或静态分析自动化的 DSL 将会增加我们对所编写代码的信心。
• 更智能的开发环境。编写、分析和测试电路是一个艰难的过程。开发过程中的语法高亮、编译时错误检查,使用 AST/witness 分析、注解(即将电路模板标记为“安全”或“不安全”,标记无约束信号)类似 IntelliSense 的工具,以及 shell / REPL 环境能够快速提升迭代速度。使用了 Kevin Kwok 的 ZKREPL 项目(包含上述若干特征),ZK 学习小组的效率得到了大大地提升。
• 构建、测试和部署工具;自动化和流水线管理。ZK 开发者目前必须手管理 ptau 文件、密钥文件、构建配置、构建文件以及发版流程。snarkjs 教程(https://github.com/iden3/snarkjs)目前列出了开发者创建和验证 zkSNARK 必须完成的26个步骤,涉及对20多个文件的手动操作。
关于如何以可访问及可审计的方式发布协议参数,目前还没有被广泛接受的最佳实践。开发者必须针对不同的环境手动维护不同版本的电路(即在测试期间关闭一些约束)。ProjectSophon 的 hardhat-circom(https://github.com/projectsophon/hardhat-circom) 和 Weijie Koh 的 circom-helper(https://www.npmjs.com/package/circom-helper) 等工具是大幅简化工流的第一大步,但仍有许多工作要做。
• 中间层表示的通用标准。不同的团队使用不同的语言和工具来编写ZK电路:circom 、arkworks、libsnark 等。理想情况下,不同工具链编写的电路应该编译为通用 IR ,以便生成证明、验证证明、审计协议设置以及其他通用任务可以与工具链无关。
对于 Groth16 来说,IR 取决于普遍认可的密码参数和 R1CS 表示的一个标准集。对于 PLONK 来说,这个问题有点复杂,因为库开发者需要弄清楚如何表示自定义约束等等。
作为钻研该领域的一个例子,Dark Forest 第三方客户端的开发激发了 Kobi Gurkan 和 gakonst 开发 ark-cirom 的动机,其连接 arkworks 和 circom 的生态。
• 易用且更高效的编译器和证明器。缓慢的密钥编译和证明会延缓开发和测试。优化编译和证明,并开发这些流程开箱即用的库(即,在大型服务器上部署远程验证器应当简便!)将节省开发者的时间。ZPrize 是一项旨在加快该工作推进的行业倡议。
• (PLONK 前)共享受信任初始化基础设施。由于信任初始化协调的困难,目前很难发布生产级 zkSNARK 应用。Zcash、 AZTEC 协议、Tornado 以及 Semaphore 都必须编写定制的受信任初始化基础设施。有一些构建可重用的受信任初始化工具的尝试,但运行这些仪式仍需耗费大量人力。
请注意,长远来看,由于我们会切换到无需每个电路进行受信任初始化的协议,这可能不会是个问题。
• (PLONK 后)SNARK 递归工具。支持递归验证的 SNARK 使我们能够构建“可编程” SNARK ,其中 SNARK 代码可以通过插入其他 SNARK 子模块的验证密钥来“即时”修改。
此外,递归 SNARK 还允许开发者并行化证明生成。真正地支持这一功能是一个难题,大概会在未来几年内解决……
审计和验证
上面列出电路十分复杂而且难以手工验证。巧妙的约束优化实际上加重了这个问题—深度优化的电路难以理解,如果你正在处理棘手的工作,那么在实现过程中很容易忽略某个约束。此外,由于 ZK 应用程序的性质,无法判断ZK电路中的 bug 是否已被外部所利用。
编写让你确信零知识证明满足完整性( completeness )的测试相当容易:证明你可以为见证者正确地由输入生成见证和见证的有效证明。确信零知识证明满足可靠性( soundness )更加困难。
为此,你必须验证给定输入存在着满足 SNARK 约束系统的见证—恶意证明者无法用一个错误的见证(生成一个并不满足约束的有效证明)瞒天过海。相比可靠性证明更难是,证明电路与规范间等价性,即形式化验证。
目前,大多数在生产中使用 ZK 电路的团队采用的方法是委托人工审计,尽管这些审计的质量参差不齐,有能力进行审计的人非常少。我们可以相当肯定像 Tornado.Cash 电路(总计仅有 100 行 circom 代码)这样的应用大概是安全的。
然而,我们的 groth16 的 ECDSA 原型实现依赖于数千行 circom 代码,有着规模为数十万或数百万个约束的电路。更复杂的原语将更难验证,而 PLONK 的自定义约束将增加额外的复杂度。
我们为 ZK 应用程序安全空间提出了几种方法。未来,我们将发布一篇博文,对我们所了解的该领域的当前方法进行更深入的概述。
• 建立一个评审员社区。团队通常难以找到拥有 ZK 应用开发经验的专家来评审他们的电路和代码。我们可以鼓励现有的专注 ZK 的团队,如 rollup 公司的工程师及应用开发者,来“有偿提供”评审,同时我们还可以开始一起培训审计员。
• 建立电路开发和评审的最佳实践。随着生态的成熟,我们希望开发用于构建、注解、归档和评审 ZK 电路的最佳实践。实现该目标的手段各不相同,自然语言规范或形式规范都有助于使电路更为清晰。列举常见的 bug 和错误(并开发一些捕捉它们的基本工具)也会对工程师和评审员有帮助。
• 电路原语正确性的手工证明。对于如哈希函数或 ECDSA 电路的重要原语,可以手写此类原语的正确性证明,这些证明可被证明检查者检查。其他的电路开发者将能够更有把握地使用这些原语。
• 自动见证唯一性验证。Ecne 是首个 R1CS 见证唯一性的自动化验证器。该项目使我们能够验证 ZK 电路是否有任何缺失的约束,这是在我们 ZK 系统中建立信心的一个重要步骤。我们希望支持和鼓励更多类似的工作。
• 基于求解器的形式验证方法。一些团队正在探索证明 ZK 电路与形式规范等效的自动化工具。这方面的工作还需要我们开发出一套通用的基准测试,以及一种用于(部分或全部)明确描述 ZK 电路的语言。
致谢
感谢 Lakshman Sankar、Yi Sun、Kobi Gurkan和 Wei Jie Koh 的反馈和校对。
特别感谢 ECN 社区翻译志愿者 @doublespending 对本文的翻译贡献。