Geometry是Kobi Gurkan 等人所在的一个新成立不久的研究组织,Kobi本人曾给ZK HACK 出过9道puzzles,这次又合作给出了这个关于Groth16延展攻击的新puzzle:ZK Hack x Geometry Puzzle I
感谢Trapdoor Tech李星老师和安比实验室Even的帮助
Geometry是Kobi Gurkan 等人所在的一个新成立不久的研究组织,Kobi本人曾给ZK HACK 出过9道puzzles,这次又合作给出了这个关于Groth16延展攻击的新puzzle:ZK Hack x Geometry Puzzle I
该puzzle的主要内容在这个github仓库中:zkhack-groth-puzzle,
英文题解参见这篇文章。
背景信息和中文题解,强烈推荐参考李星老师的文章。
SNARK领域中的延展攻击,是指给定敌手一个合法证明,敌手在不知道见证的条件下自己生成新的(对相同或不同公开输入的)合法证明。
对于相同公开输入的伪造证明的方法请参见这篇文章How to Generate a Groth16 Proof for Forgery,但是其中描述的两个攻击对于需要更改公开输入到特定值(如本题为攻击者的以太坊地址)时并不适用。
论文BKSV20 中的如下定理给出了Groth16抵抗延展攻击的一个条件:
定理:如果${ui(x)}{i=0}^l$ 是线性独立的,且 $Span{ui(x)}{i=0}^l \cap Span{ui(x)}{i=l+1}^m=\emptyset$。则Groth16在代数群模型中,离散对数假设下,可达到弱白盒模拟可抽取性质。
如果条件“${ui(x)}{i=0}^l$ 是线性独立的,且 $Span{ui(x)}{i=0}^l \cap Span{ui(x)}{i=l+1}^m=\emptyset$ ”不满足,则可以构造更改公开输入的延展攻击。本文下文即描述在不满足该条件时如何利用该点进行延展攻击伪造证明。
固定某个$k\in[l+1..m],j\in[0..l]$(分别是见证和公开输入的下标),使得多项式$u_k(x)$,$v_k(x)$,$w_k(x)$分别和$u_j(x)$,$v_j(x)$,$w_j(x)$ 线性相关。记:
$$ W_k(x)= \betau_k(x)+ \alphav_k(x)+w_k(x) $$
$$ PI_j(x)= \betau_j(x)+ \alphav_j(x)+w_j(x) $$
于是 $W_k(x)$和 $PI_j(x)$也线性相关,即存在某个线性因子$lf$使得:
$$ lf\cdot W_k(x)=PI_j(x) $$
Groth16的验证方程如下:
$$ [A]_1\cdot [B]_2 = [\alpha]_1 \cdot [\beta]2 + \sum{i=0}^l z_i \Bigg[\cfrac{\beta u_i(x)+\alpha v_i(x)+w_i(x)}{\gamma}\Bigg]_1 \cdot [\gamma]_2 + [C]_1 \cdot [\delta]_2 $$
把$PI_j(x)$项分离出来后如下:
$$ [A]_1\cdot [B]_2 = [\alpha]_1 \cdot [\beta]2 + \sum{^{i=0}_{i\neq\ j}}^l z_i \Bigg[\cfrac{\beta u_i(x)+\alpha v_i(x)+w_i(x)}{\gamma}\Bigg]_1 \cdot [\gamma]_2 +z_j \Bigg[\cfrac{PI_j(x)}{\gamma}\Bigg]_1 \cdot [\gamma]_2+ [C]_1\cdot [\delta]_2 $$
如果只考虑配对操作后$G_T$群中的指数,验证方程如下:
$$ Result=\alpha\beta+\sum_{i=0}^l z_i (\beta u_i(x)+\alpha v_i(x)+wi(x))+C{paired}(x) $$
注意这种形式下未知的分母$\gamma$已经被消去了。
如果更改公开输入值$z_j $为$z^\prime_j $,结果变为:
$$ Result^\prime=Result+z^\prime_j PI_j(x)-z_j PI_j(x)=Result+(z^\prime_j-z_j)*PI_j(x) $$
和原始$Result$差了一项$(z^\prime_j-z_j)PI_j(x)$。通过修改方程右式中的 $C$ 如下,则可以在方程右边平衡左边原始结果多出来的$(z^\prime_j-z_j)PI_j(x)$:
$$ C^\prime=C+lf(z^\prime_j-z_j)\Bigg[\cfrac{W_k(x)}{\delta}\Bigg]_1 $$
注意上式使用了线性相关关系:
$$ lf\cdot W_k(x)=PI_j(x) $$
其中
$$ \Bigg[\cfrac{W_k(x)}{\delta}\Bigg]_1=\Bigg[\cfrac{\beta u_k(x)+\alpha v_k(x)+w_k(x)}{\delta}\Bigg]_1 $$
在公开参数中已知可用。而公开参数里存储的是
$$ \Bigg[\cfrac{PI_j(x)}{\gamma}\Bigg]_1=\Bigg[\cfrac{\beta u_j(x)+\alpha v_j(x)+w_j(x)}{\gamma}\Bigg]_1 $$
不能被直接使用。
于是有:
$$ [A]_1\cdot [B]_2 = [\alpha]_1 \cdot [\beta]2 + \sum{i=0}^l z_i \Bigg[\cfrac{\beta u_i(x)+\alpha v_i(x)+w_i(x)}{\gamma}\Bigg]_1 \cdot [\gamma]_2 +z^\prime_j \Bigg[\cfrac{PI_j(x)}{\gamma}\Bigg]_1\cdot [\gamma]_2+C^\prime(x)\cdot [\delta]_2 $$
$$ =[\alpha]_1 \cdot [\beta]2 + \sum{^{i=0}_{i\neq\ j}}^l z_i \Bigg[\cfrac{\beta u_i(x)+\alpha v_i(x)+w_i(x)}{\gamma}\Bigg]_1 \cdot[\gamma]_2+z^\prime_j \Bigg[\cfrac{PI_j(x)}{\gamma}\Bigg]_1\cdot [\gamma]_2+C(x) \cdot[\delta]_2+l(z_j-z^\prime_j )\Bigg[\cfrac{W_k(x)}{\delta}\Bigg]_1\cdot [\delta]_2 $$
依然只考虑配对操作后 $G_T$ 群中的指数,上式等于:
$$ =\alpha\beta+\sum{^{i=0}{i\neq\ j}}^l z_i (\beta u_i(x)+\alpha v_i(x)+w_i(x))+z^\prime_j PIj(x)+C{paired}(x)+l(z_j -z^\prime_j)*W_k(x) $$
$$ =\alpha\beta+\sum{^{i=0}{i\neq\ j}}^l z_i (\beta u_i(x)+\alpha v_i(x)+w_i(x))+z^\prime_j PIj(x)+C{paired}(x)+(z_j -z^\prime_j)PI_j(x) $$
$$ =\alpha\beta+\sum{^{i=0}{i\neq\ j}}^l z_i (\beta u_i(x)+\alpha v_i(x)+w_i(x))+z^\prime_j PIj(x)+C{paired}(x)+z_j PI_j(x)-z^\prime_j *PI_j(x) $$
$$ =\alpha\beta+\sum{^{i=0}{i\neq\ j}}^l z_i (\beta u_i(x)+\alpha v_i(x)+wi(x))+C{paired}(x)+z_j *PI_j(x) $$
$$ =\alpha\beta+\sum_{^{i=0}}^l z_i (\beta u_i(x)+\alpha v_i(x)+wi(x))+C{paired}(x) $$
注意到最后一行正是原始验证方程的右式,这意味着敌手改变了公共输入,并对新的语句伪造出了合法的证明,即成功进行了延展攻击。
具体到本题电路中,公开输入a
和见证b
的相关约束为:$0=b-a$ , 公开输入只有$z_0=1$,$z_1=a$,$l=1$。
$u_1(x)=v_1(x)=u_2(x)=v_2(x)=0,w_1(x)=-w_2(x)$
$$ C^\prime=C+lf(z^\prime_1-z_1)\Bigg[\cfrac{\beta u_2(x)+\alpha v_2(x)+w_2(x)}{\delta}\Bigg]_1 $$
其中$z^\prime_1$ = new_a
,$ z_1$=a
,$lf=-1$
const buildMalleabeC = async (stringified_c, matching_base_index, matching_pub_input, new_public_input, lf) => {
const c = unstringifyBigInts(stringified_c)
const {fd: fdZKey, sections: sectionsZKey} = await binFileUtils.readBinFile(finalZkeyPath, "zkey", 2, 1<<25, 1<<23)
const buffBasesC = await binFileUtils.readSection(fdZKey, sectionsZKey, 8)
fdZKey.close()
const curve = await buildBn128();
const Fr = curve.Fr;
const G1 = curve.G1;
const new_pi = new Uint8Array(Fr.n8);
Scalar.toRprLE(new_pi, 0, new_public_input, Fr.n8);
const matching_pub = new Uint8Array(Fr.n8);
Scalar.toRprLE(matching_pub, 0, matching_pub_input, Fr.n8);
const sGIn = curve.G1.F.n8*2
const matching_base = buffBasesC.slice(matching_base_index*sGIn, matching_base_index*sGIn + sGIn)
const linear_factor = Fr.e(lf.toString(10))
const delta_lf = Fr.mul(linear_factor, Fr.sub(matching_pub, new_pi));
const p = await curve.G1.timesScalar(matching_base, delta_lf);
const affine_c = G1.fromObject(c);
const malleable_c = G1.toAffine(G1.add(affine_c, p))
return stringifyBigInts(G1.toObject(malleable_c))
}
const linearDep = BigInt(-1)
const matchingBase = 0;
const malleable_c = await buildMalleabeC(proof.pi_c, matchingBase, BigInt(a), new_a, linearDep)
proof.pi_c = malleable_c;
[1] Geometry: https://geometryresearch.xyz/about [2] Kobi Gurkan: https://twitter.com/kobigurk [3] ZK HACK: https://zkhack.dev/events/ [4] ZK Hack x Geometry Puzzle I: https://geometryresearch.xyz/notebook/zkhack-groth-challenge [5] zkhack-groth-puzzle: https://github.com/geometryresearch/zkhack-groth-puzzle [6] 这篇: https://geometry.xyz/notebook/groth16-malleability [7] How to Generate a Groth16 Proof for Forgery: https://medium.com/ppio/how-to-generate-a-groth16-proof-for-forgery-9f857b0dcafd [8] BKSV20: https://eprint.iacr.org/2020/811
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!