零知识证明安全实现经验

1. 引言

JP Aumasson(BLAK hash function family论文的作者之一),在2022年的zkStudyClub中分享了其审计多个ZKP项目中发现的各种隐患和bug。

主要关注的为:

  • Fully succinct = O ( 1 ) =O(1) =O(1) proof size and O ( circuit size ) O(\text{circuit size}) O(circuit size) verification time的zkSNARKs方案

主要基于审计以下项目的经验:

  • Groth16:ZCash、Filecoin等。
  • Marlin:Aleo使用的,为universal zkSNARKs。

这些经验也可用于其它如Plonk、SONIC、STARK等系统。

2. 为何需要学习zkSNARKs安全?

对于区块链项目,zkSNARKs的主要风险在于:

  • 复杂性+创新性 会引起 non-trivial bugs
  • 事关重大(很多钱$ 以及 用户数据和隐私)

作为一名密码学家,当今最有趣的密码:

  • 解决现实世界问题,并大规模部署(好论文+好代码!)
  • 具有重要组件的复杂结构
  • “简单但复杂”(非交互式,很多moving部件)
  • 关于安全的多维思考

3. 什么是zkSNARKs安全?

  • 1)soundness,通常为实践中的最大风险:
    • invalid proofs应总是被拒绝。
    • 应无法伪造、修改、重放valid proofs。
  • 2)Zero-knowledge:proofs不应泄露witness信息(私有变量)
    • 实践中,large programs的succinct proof仅可泄露一点点数据。
  • 3)Completeness:通常DoS/可用性风险可能会被进一步利用:
    • valid proofs应总是被接受。
    • 所支持的所有programs/circuits应被正确处理。

4. 审计ZKP项目的主要挑战及策略

由于近些年才有实用zkSNARKs,审计者审计ZKP项目的主要挑战在于:

  • 1)审计ZKP项目的经验有限
  • 2)对理论和实现技巧的了解有限
  • 3)bug和bug类别的“checklist”有限
  • 4)工具和方法有限

大多数bug是由内部或类似项目的团队发现。
为此,对于zkSNARKs这种新密码学应用,要采用新方法来审计:

  • 1)与开发者和设计者紧密合作(如联合review、Q&A等)。
  • 2)更多威胁分析,以了解应用程序的独特/新风险。
  • 3)更多实践经验:写PoC、circuit、proof system等。
  • 4)学习之前的失败案例,来源不限于:
    • public disclosures and exploits
    • 其他审计报告
    • 跟踪issue和PR
    • 社区

5. ZKP总体流程及失败案例

在这里插入图片描述
与第3)节的zkSNARKs安全定义呼应,破坏zkSNARKs安全主要体现在:

  • 1)破坏soundness,如利用:
    • Contraint system无法有效地强化特定constraints。
    • proving keys未安全生成 或 未安全保护。
  • 2)破坏zero-knowledge,如利用:
    • 将私有数据当成了公开变量。
    • 协议层面的"metadata attack"。
  • 3)破坏completeness,如利用:
    • 边缘情况下R1CS合成行为不正确(例如,关于私有变量数)
    • gadget i/o值与类型不匹配引起的gadget组合失败
  • 4)破坏(链下)软件,通过任意bug导致:
    • 数据泄露,包括通过side channels、encodings(“ZK execution”)。
    • 任何形式的不安全状态(code execution, DoS)。
  • 5)通过以下方式,使supply-chain被compromise:【即各依赖环节】
    • Truste setup的代码和execution.
    • build and release process integrity
    • software dependencies
  • 6)通过合约bug、逻辑缺陷等破坏(链上)软件(包括verifier)。

将ZKP系统分层表示为:
在这里插入图片描述

  • 1)下层的故障可能会危及所有上层的安全:
    在这里插入图片描述
  • 2)子组件中的故障可能会危及所有上层的安全:
    在这里插入图片描述
  • 3)Security 101:必须定义、实现并测试输入的有效性:
    在这里插入图片描述

5.1 Filed arithmetic实现引起的soundness问题

Filed arithmetic实现引起的soundness问题的案例主要有:

 uint256 snark_scalar_field = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
    VerifyingKey memory vk = verifyingKey();
    require(input.length + 1 == vk.IC.length, "verifier-bad-input");
    // Compute the linear combination vk_x
    Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0);
    for (uint256 i = 0; i < input.length; i++) {
    // 注意此处!做overflow检查。
      require(input[i] < snark_scalar_field, "verifier-gte-snark-scalar-field");
      vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[i + 1], input[i]));
    }

5.2 R1CS引起的soundness问题

arkworks-rs的r1cs-std项目中的constraint system未对Field element inversion属性进行强化,从而引发soundness问题。

	/// Returns `(self / d)`.
    /// The constraint system will be unsatisfiable when `d = 0`.
    fn mul_by_inverse(&self, d: &Self) -> Result<Self, SynthesisError> {
    
    
        // Enforce that `d` is not zero.
        d.enforce_not_equal(&Self::zero())?;
        self.mul_by_inverse_unchecked(d)
    }

    /// Returns `(self / d)`.
    ///
    /// The precondition for this method is that `d != 0`. If `d == 0`, this
    /// method offers no guarantees about the soundness of the resulting
    /// constraint system. For example, if `self == d == 0`, the current
    /// implementation allows the constraint system to be trivially satisfiable.
    fn mul_by_inverse_unchecked(&self, d: &Self) -> Result<Self, SynthesisError> {
    
    
        let cs = self.cs().or(d.cs());
        match cs {
    
    
            // If we're in the constant case, we just allocate a new constant having value equalling
            // `self * d.inverse()`.
            ConstraintSystemRef::None => Self::new_constant(
                cs,
                self.value()? * d.value()?.inverse().expect("division by zero"),
            ),
            // If not, we allocate `result` as a new witness having value `self * d.inverse()`,
            // and check that `result * d = self`.
            _ => {
    
    
                let result = Self::new_witness(ark_relations::ns!(cs, "self  * d_inv"), || {
    
    
                    Ok(self.value()? * &d.value()?.inverse().unwrap_or(F::zero()))
                })?;
                result.mul_equals(d, self)?;
                Ok(result)
            },
        }
    }

5.3 hash validation引起的soundness问题

详细参看2019年博客 Tornado.cash got hacked. By us.
iden3的circomlib中实现MIMC哈希函数的编码错误,Tornado使用circomlib库来构建deposit merkle tree,该编码错误使得可伪造witness的Merkle root并伪造证明。
在这里插入图片描述

5.4 论文理论错误引起的soundness问题

BCTV14-Succinct Non-Interactive Zero Knowledge for a von Neumann Architecture 论文中setup描述中的理论缺陷(未清除敏感数据)引起的soundness问题,详细见ZCash 2019年博客Zcash Counterfeiting Vulnerability Successfully Remediated

5.5 应用中未正确设置nonce值引起的zero-knowledge问题

Aztec中,因未正确设置nonce值引起的zero-knowledge问题,从而破坏了隐私性。详细见博客 Aztec 2.0 Pre-Launch Notes
在这里插入图片描述

5.6 应用中(shielded)交易之间的关联性引起的zero-knowledge问题

ZCash中(shielded)交易之间的关联性泄露有利用价值的信息,详细见2020年论文 Attacking Zcash Protocol For Fun And Profit
在这里插入图片描述

5.7 Prover端缺少(randomized)blinding引起的zero-knowledge问题

dusk-network的plonk中,Prover端缺少(randomized)blinding来隐藏私有输入,从而存在潜在的ZK loss。
在这里插入图片描述

5.8 Merkle tree中不完整的tree constraints引起的DoS问题

aztec中的不完整tree constraints,会引起rollup validation的冻结,从而造成DoS问题。
详细见2021年博客Vulnerabilities patched in Aztec 2.0
在这里插入图片描述

5.9 DSL/签名 引起的Dos/Completeness问题

starkware-libs的cairo-lang项目中的有效签名被拒问题,该风险初期认为是可忽略的,详细见:

在这里插入图片描述

5.10 其它类型的bug

  • 1)密码学问题,如:
  • 2)组合性问题:嵌套proof systems之间的不安全交互。
  • 3)Side channels问题: Non-ct code, RAM leakage, speculative execution leaks(推测性执行泄露)

参考资料

[1] zkStudyClub: Zero-Knowledge Proofs Security, in Practice [JP Aumasson, Taurus]

猜你喜欢

转载自blog.csdn.net/mutourend/article/details/124073005
今日推荐