零知识证明 - Trapdoor团队发现PoREP严重漏洞

Trapdoor团队发现PoREP电路V25版本存在严重漏洞。利用该零知识证明的漏洞,SDR算法(Precommit1)的计算可以直接省略。所有Sector的Replica的数据也只要存储一份。Trapdoor团队在第一时间和官方沟通后,官方已经快速提交补丁:

PoREP电路也从V25版本,升级到V26。本文仔细讲讲该严重漏洞的攻击原理。

众所周知,Sector数据会经过Labeling, Column Hash以及Encoding计算生成最后的Replica数据(Precommit1和Precommit2阶段)。整个计算通过零知识证明生成证明,并提交到链上。将Sector中的所有节点数据的处理都进行证明不太现实,对应的电路会非常的大。Lotus目前的实现是随机抽查144个节点数据,将这144个节点数据的处理做成电路。即使是这样,电路规模也已经非常大,超过1亿。

1. replica_id介绍

replica_id是每个Replica数据的标示。计算方法如下:

let replica_id =
    generate_replica_id::<Tree::Hasher, _>(&prover_id, sector_id.into(), &ticket, comm_d);

也就是说,replica_id是prover_id, sector_id, comm_d以及链上随机数ticket的hash结果。其中,prover_id是矿工的id,sector_id是Sector的编号,依次增长,comm_d是Sector原始数据的merkle树根。很容易发现,每个Sector对应的replica_id是不一样的。

2. SDR算法计算

SDR算法的计算过程,也就是Labeling的过程。SDR的每个节点的计算都需要replica_id,具体的计算过程如下:

3. PoREP电路

在Sector进行Labeling, Column Hash以及Encoding计算后,随机挑选144个节点数据生成电路,并进行零知识证明。每个节点对应的电路逻辑如下:

每个节点,都需要正确地经过Labeling,Column Hash以及Encoding计算。并且能计算出对应的Merkle树树根。

3.1 随机抽查节点的选择

抽查节点的选择逻辑实现在storage-proofs/porep/src/stacked/vanilla/challenges.rs的LayerChallenges结构的derive_internal函数:

let j: u32 = ((challenges_count * k as usize) i) as u32;
let hash = Sha256::new()
    .chain(replica_id.into_bytes())
    .chain(seed)
    .chain(&j.to_le_bytes())
    .result();

也就是说,抽查的节点是replica_id,随机数以及抽查编号的hash计算结果。简单的说,节点的抽查和replica_id相关。

3.2 PoREP电路的公开输入

整个PoREP的证明电路的公开输入除了comm_d和comm_r外,还有各个Merkle树的路径信息。也就是,整个电路限定了抽查的节点的位置,原始数据的comm_d和最终Replica数据的comm_r。也只是限制了这些信息。

4. 漏洞在哪里?

仔细分析PoREP电路的公开输入发现,PoREP电路的公开输入中,并没有包括replica_id。虽然抽查节点的位置和replica_id有间接的关系,在replica_id没有作为公开输入的情况下,SDR的计算可以采用伪造的replica_id。简单的说,在计算过程中,可以采用任意的replica_id进行Labeling计算。由此,攻击方法就清晰了:

因为Labeling的计算,只和replica_id有关,在采用伪造的replica_id的情况下,整个SDR的计算只需要提前计算好即可。也就是说,在这个漏洞的攻击下,SDR算法毋需计算。

Trapdoor团队,威武!向专注在零知识证明的技术团队致敬!

总结:

Trapdoor团队发现V25的PoREP电路存在严重漏洞。利用该漏洞,SDR(Precommit1)的计算可以省略。PoREP电路的公开输入中,并没有包括replica_id。虽然抽查节点的位置和replica_id有间接的关系,在replica_id没有作为公开输入的情况下,SDR的计算可以采用伪造的replica_id。

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。