ZKsync加密预编译审计

本篇文章对Matter Labs的zksync-crypto库进行了详尽的审计,重点指出多项高严重性问题,包括实现错误、缺少安全检查等,并提出了一些优化建议。审计内容广泛,深入探讨了加密算法的实现细节及其对ZKsync的影响。

目录

摘要

TypePrecompileTimelineFrom 2025-02-25To 2025-03-12LanguagesRustTotal Issues9 (8 resolved)Critical Severity Issues0 (0 resolved)High Severity Issues5 (5 resolved)Medium Severity Issues1 (1 resolved)Low Severity Issues2 (2 resolved)Notes & Additional Information1 (0 resolved)

范围

我们审计了 matter-labs/zksync-crypto 存储库中的 pull request #77,提交 ccf5fa3

审计范围包括以下文件:

 crates
└── boojum
    └── src
        └── gadgets
            ├── curves
            │   ├── sw_projective
            │   │   └── mod.rs
            │   ├── zeroable_affine
            │   │   └── mod.rs
            ├── non_native_field
            │   ├── implementations
            │   │   ├── impl_traits.rs
            │   │   ├── implementation_u16.rs
            │   │   └── mod.rs
            │   └── traits
            │       └── mod.rs
            ├── tower_extension
            │   ├── algebraic_torus.rs
            │   ├── fq12.rs
            │   ├── fq2.rs
            │   ├── fq6.rs
            │   ├── mod.rs
            │   └── params
            │       ├── bn256.rs
            │       └── mod.rs
            ├── traits
            │   ├── hardexp_compatible.rs
            │   └── mod.rs
            ├── u256
            │   └── mod.rs
            └── u512
                └── mod.rs
└── pairing
    └── src
        └── bn256
            ├── ec.rs
            └── mod.rs
            └── pairing_certificate.rs

系统概述

待审查的代码是 Matter Labs 的 zksync-crypto 存储库中 pairingboojum crates 的一部分,这是一个支持 ZKsync——以太坊的Layer2扩展解决方案的加密库。这些 crates 使用 BN256 曲线实现椭圆曲线配对,这也称为 alt_bn128 或在不同上下文中称为 BN254。它们支持 zk-SNARKs,这对于 ZKsync 的零知识证明至关重要,安全高效地增强以太坊交易扩展。

pairing crate 位于 crates/pairing/src/bn256 中,通过 ec.rsmod.rs 等文件处理 BN256 曲线的算术运算。它在主机系统上原生计算配对,支持 ZKsync 的 rollup 证明和签名验证,超出算术电路。审核的代码在 pairing crate 中使用预计算的线函数用于米勒循环,米勒循环是配对计算的关键步骤,以增强 zk-SNARK 验证的性能。这种方法通过减少配对操作的计算成本来提高效率。

boojum crate 位于 crates/boojum/src 中。它专注于基于电路的 BN256 操作,包括在 gadgets/curves 中的仿射和投影曲线算术,以及在 gadgets/tower_extensionalgebraic_torus.rs 文件中的塔域扩展,以及在 gadgets/non_native_field 中的非原生域算术。这个 crate 将配对计算编码为算术电路,使 ZKsync 的证明者能够生成 zk-SNARK 证明。审核的增强通过代数托罗斯算术等技术优化这些电路,从而减少证明生成的复杂性和成本。

安全模型与特权角色

本次审计目标是上面列出的文件(在 crates/pairing/src/bn256crates/boojum/src),这些文件有依赖项超出本次审计的范围。虽然我们审查了依赖项,但未更改的部分无法可靠地被认为是正确的。

pairing crate 假设存在一个安全的本地环境,在信任预计算的米勒循环函数的前提下进行 rollup 证明和签名。boojum crate 使用算术电路,假设存在一个可信的证明者-验证者设置,其中证明者可以访问见证数据,但确保可验证的零知识证明。两个 crate 都信任 BN256 曲线的配对困难性,没有额外角色,超出标准的加密假设(例如,诚实计算)。

然而,随着 塔域筛法技术 的进步,BN256 的有效安全性因这些进展而降至约 100 位。因此,尽管在实务中没有被打破,BN254 由于这一缺陷在未来使用中并不合适。

高严重性

偶次幂的 pow_u32 指数运算实现不正确

algebraic_torus.rs 文件中,函数 pow_u32<CS, S: AsRef<[u64]>>(&mut self, cs: &mut CS, exponent: S)S=2^k 时,对于所有的 k,输出 γg\frac{\gamma}{g}gγ​不正确,原因是从 Self::zero 和静态的 base 开始。这种错误在 $S$ 为偶数的所有情况下都存在,破坏了在这些情况下的指数运算。如果在配对计算中使用,这可能导致不正确的配对评估,可能允许虚假的 zk 证明。

考虑使用第一个比特进行预计算,然后从第二个比特开始,在每次迭代中更新 base

更新: 已解决,详见 PR #87 的提交 65c890d

G2 点缺少子群检查

pairing_certificate.rs 中,G2 中的点在配对操作中使用,但没有明确验证它们是否属于正确的质数阶子群。如果一个点 (x,y)(x, y)(x,y) 不在这个子群中,配对计算可能会产生不正确的结果,可能会影响证明的有效性。

考虑通过确保 [r]P=O[r]P = O[r]P=O 来实现子群验证检查,其中 ( r ) 是子群的质数阶,( O ) 是单位元素。一种更快速的强制方法是检查在 这篇 论文中 Proposition 3 的条件。

更新: 已解决。这不是一个问题。Matter Labs 团队澄清说,子群检查被强制执行在电路级别,并且由于这段代码仅用于生成见证,因此生成见证过程本身并不强制检查子群。

未实现的 allocate_constant 导致 fq2.rs 中的 panic

CSAllocatable 特性中的 allocate_constant 函数未实现。

这导致在调用 allocate_constantfq2 分配常量时产生 panic,导致证明生成无法完成。

考虑通过适度包装内部常量分配覆盖 fq2.rs 中的 allocate_constant,或实现该函数。

更新: 已解决,详见 pull request #88 的提交 09cbca4

塔扩展的逆计算中缺乏零检查

fq2.rsfq6.rsfq12.rs 中,逆函数目前并未检查该元素是否为零。

这可能导致尝试反转零元素时出现未定义行为。

考虑在相关域中添加零元素的检查,并包括错误管理以在元素为零时返回错误。

更新: 已解决,详见 pull request #89 的提交 15b2ca0

fq.rs 中 NEGATIVE_ONE 常量的计算不正确

fq.rs 文件中,常量 NEGATIVE_ONE 的计算不正确,应该表示 (−((2256)mod  q)mod  q)(-((2^{256}) \mod q) \mod q)(−((2256)modq)modq)。

这个常量用于域扩展定义,可能导致在涉及它的域扩展和算术运算中出现错误,导致不正确的结果。

考虑将 NEGATIVE_ONE 更正为其正确值:['0x68c3488912edefaa', '0x8d087f6872aabf4f', '0x51e1a24709081231', '0x2259d6b14729c0fa']

更新: 已解决,详见 pull request #90 的提交 729cf0a

中等严重性

转换一个可能的 512 位数字为 256 位数字,导致 panic

crates/boojum/src/gadgets/u256/mod.rs 中,第391行代码如下:

let q: U256 = q.try_into().unwrap();

此处,q 等于 ⌊abm⌋\lfloor \frac{ab}{m} \rfloor⌊mab​⌋,其中 aaa 和 bbb 为 256 位数字。由于 q 可能达到 512 位,转换为 U256 可能失败。如果失败,unwrap() 将导致 modmul 函数 panic,干扰证明流程。

考虑将 q 使用 U512,或添加错误处理以避免 panic。

更新: 已解决,详见 pull request #92

低严重性

静默调试断言 utils.rs 在发布模式下

在文件 utils.rs 的第8行中,含有多个调试断言(例如,debug_assert!)。在发布模式下编译代码时,这些断言会被禁用,这是 Rust 的标准行为。因此,这些断言检查的条件在生产执行过程中不会得到验证,可能允许错误在 boojum crate 中悄然发生。

在加密证明中,算术的正确性对证明生成和验证至关重要,静默失败可能导致无效的证明或性能问题。

考虑在发布模式中将 debug_assert! 替换为 assert!

更新: 已解决,详见 pull request #94

线系数和点运算中的冗余计算

在当前的 pairing_certificate.rs 文件中的线函数计算实现中,斜率 alphaline_doubleline_add 函数中分别计算。同时,点运算如 t.double()t.add_assign_mixed() 会重新计算 alpha,而不是重复使用该值。

这导致冗余的字段算术运算,降低了配对和证明验证的计算效率。

考虑将 alphamu 的计算直接集成到点运算中,以最小化重复计算并允许重复使用中间结果。

更新: 已解决,详见 pull request #93

注释与附加信息

代码库中的错误注释和打字错误

在整个代码库中,我们发现以下打字错误和不正确的注释,需要予以修正:

  1. fq.rs 中,第103行错误地表述为 Fq2(u + 1)**(((2q^0) - 2) / 3)。正确的表达应为 Fq2(u + 9)**(((2q^0) - 2) / 3)。这一更正也适用于以下行:103, 108, 113, 118, 123, 128, 137, 142, 147, 152, 157, 162, 167, 172, 177, 182, 187, 和 192

  2. sw_projective/mod.rs 中,第213行的注释应更正为:// y3 = y3 + t0

  3. ec.rs 中,第592行应更新为:unimplemented!("on curve check is not implemented for BN-256 projective")

  4. pairing_certificate.rs 中,为了澄清第118行的注释,应将其更改为:// and compute c0 = 1, c3 = - lambda * p.x / p.y = lambda * x', c4 = - mu / p.y = mu * y'

考虑应用这些更正,以帮助提高代码可读性,并防止未来的困惑,尤其是对于从事加密函数和 BN-256 曲线工作的开发人员。

更新: 已确认,将予以解决。Matter Labs 团队表示他们希望推迟修复,并将其纳入即将的改进中。

建议

优化 algebraic_torus.rs 中的 decompress 函数

在文件 algebraic_torus.rs 中,decompress 函数可以根据以下观察进行优化:

给定 g+wg−w=g2+(u+9)g2−(u+9)+2gg2−(u+9)w, \frac{g + w}{g - w} = \frac{g^2 + (u + 9)}{g^2 - (u + 9)} + \frac{2g}{g^2 - (u + 9)} w, g−wg+w​=g2−(u+9)g2+(u+9)​+g2−(u+9)2g​w,且注意到 d=u+9d = u + 9d=u+9 是一个已经预计算的常数,表达式可以进一步简化为 1+2D−1(d+gw), 1 + 2D^{-1}(d + gw), 1+2D−1(d+gw),其中, D=g2−dD = g^2 -dD=g2−d 是分母。

这种方法利用了表达式的特殊结构,通过最小化乘法和冗余操作来减少约束和计算开销,从而提高配对计算和证明验证的效率。

实施步骤

  1. 将预计算常数 d=u+9d = u + 9d=u+9 作为常量导入 Fp2\mathbb{F}_{p^2} Fp2​。
  2. 计算 D=g2−dD = g^2 - dD=g2−d(在 Fp6\mathbb{F}_{p^6} Fp6​ 中进行一平方和一减法)。
  3. 计算 D−1D^{-1}D−1(在 Fp6\mathbb{F}_{p^6} Fp6​ 中进行一次反演)。
  4. 在 Fp6\mathbb{F}_{p^6} Fp6​ 中计算 2D−12D^{-1}2D−1(一次加倍操作)。
  5. 在 Fp2\mathbb{F}_{p^2} Fp2​ 中添加人1。

结论

审核代码提供了一个基于配对的原生签名生成的加密库,约束使用非原生字段算术的加密运算,以及支持场扩展的电路。

在审计过程中,识别出几项高严重性问题。此外,审计还发现了几个改善领域,特别是在测试、错误处理和文档方面。更全面的单元测试套件将有助于确保所有操作的正确性、边缘情况得到适当处理,以及识别任何静默或未实现的错误。虽然某些错误,例如零除错误,已通过电路隐式管理,但明确记录这些情况将提高清晰度和稳健性。额外的文档还可以阐明某些设计选择背后的理由,特别是在电路内模拟非原生字段算术使用的复杂溢出跟踪和减少/归一化方案。尽管这些方面并未立即威胁系统安全,解决这些问题将增强协议的可靠性和可维护性。

尽管发现了这些问题,与团队的沟通显著快速且友好,我们感谢 Matter Labs 团队在此项目上的合作。

请求审计

  • 原文链接: blog.openzeppelin.com/zk...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
OpenZeppelin
OpenZeppelin
江湖只有他的大名,没有他的介绍。