本篇文章对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
存储库中 pairing
和 boojum
crates 的一部分,这是一个支持 ZKsync——以太坊的Layer2扩展解决方案的加密库。这些 crates 使用 BN256 曲线实现椭圆曲线配对,这也称为 alt_bn128 或在不同上下文中称为 BN254。它们支持 zk-SNARKs,这对于 ZKsync 的零知识证明至关重要,安全高效地增强以太坊交易扩展。
pairing
crate 位于 crates/pairing/src/bn256
中,通过 ec.rs
和 mod.rs
等文件处理 BN256 曲线的算术运算。它在主机系统上原生计算配对,支持 ZKsync 的 rollup 证明和签名验证,超出算术电路。审核的代码在 pairing
crate 中使用预计算的线函数用于米勒循环,米勒循环是配对计算的关键步骤,以增强 zk-SNARK 验证的性能。这种方法通过减少配对操作的计算成本来提高效率。
boojum
crate 位于 crates/boojum/src
中。它专注于基于电路的 BN256 操作,包括在 gadgets/curves
中的仿射和投影曲线算术,以及在 gadgets/tower_extension
和 algebraic_torus.rs
文件中的塔域扩展,以及在 gadgets/non_native_field
中的非原生域算术。这个 crate 将配对计算编码为算术电路,使 ZKsync 的证明者能够生成 zk-SNARK 证明。审核的增强通过代数托罗斯算术等技术优化这些电路,从而减少证明生成的复杂性和成本。
本次审计目标是上面列出的文件(在 crates/pairing/src/bn256
和 crates/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。
在 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_constant
为 fq2
分配常量时产生 panic,导致证明生成无法完成。
考虑通过适度包装内部常量分配覆盖 fq2.rs
中的 allocate_constant
,或实现该函数。
更新: 已解决,详见 pull request #88 的提交 09cbca4。
在 fq2.rs、fq6.rs 和 fq12.rs 中,逆函数目前并未检查该元素是否为零。
这可能导致尝试反转零元素时出现未定义行为。
考虑在相关域中添加零元素的检查,并包括错误管理以在元素为零时返回错误。
更新: 已解决,详见 pull request #89 的提交 15b2ca0。
在 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。
在 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
文件中的线函数计算实现中,斜率 alpha
在 line_double
和 line_add
函数中分别计算。同时,点运算如 t.double()
和 t.add_assign_mixed()
会重新计算 alpha
,而不是重复使用该值。
这导致冗余的字段算术运算,降低了配对和证明验证的计算效率。
考虑将 alpha
和 mu
的计算直接集成到点运算中,以最小化重复计算并允许重复使用中间结果。
更新: 已解决,详见 pull request #93。
在整个代码库中,我们发现以下打字错误和不正确的注释,需要予以修正:
在 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。
在 sw_projective/mod.rs
中,第213行的注释应更正为:// y3 = y3 + t0
在 ec.rs
中,第592行应更新为:unimplemented!("on curve check is not implemented for BN-256 projective")
在 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)2gw,且注意到 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 是分母。
这种方法利用了表达式的特殊结构,通过最小化乘法和冗余操作来减少约束和计算开销,从而提高配对计算和证明验证的效率。
审核代码提供了一个基于配对的原生签名生成的加密库,约束使用非原生字段算术的加密运算,以及支持场扩展的电路。
在审计过程中,识别出几项高严重性问题。此外,审计还发现了几个改善领域,特别是在测试、错误处理和文档方面。更全面的单元测试套件将有助于确保所有操作的正确性、边缘情况得到适当处理,以及识别任何静默或未实现的错误。虽然某些错误,例如零除错误,已通过电路隐式管理,但明确记录这些情况将提高清晰度和稳健性。额外的文档还可以阐明某些设计选择背后的理由,特别是在电路内模拟非原生字段算术使用的复杂溢出跟踪和减少/归一化方案。尽管这些方面并未立即威胁系统安全,解决这些问题将增强协议的可靠性和可维护性。
尽管发现了这些问题,与团队的沟通显著快速且友好,我们感谢 Matter Labs 团队在此项目上的合作。
- 原文链接: blog.openzeppelin.com/zk...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!