From 3fce6c5a689b49c81a9088f20584c56fac4ce39a Mon Sep 17 00:00:00 2001 From: munna0908 Date: Tue, 9 Jun 2026 12:17:51 +0530 Subject: [PATCH 1/2] avoid assert for input validations --- groth16/prover/groth16.nim | 22 +++++---- groth16/verifier.nim | 15 ++++-- tests/groth16/testProver.nim | 91 ++++++++++++++++++++++++++++++++++-- 3 files changed, 110 insertions(+), 18 deletions(-) diff --git a/groth16/prover/groth16.nim b/groth16/prover/groth16.nim index f06fee2..0bc03ad 100644 --- a/groth16/prover/groth16.nim +++ b/groth16/prover/groth16.nim @@ -31,24 +31,26 @@ import groth16/prover/shared # the prover # -proc generateProofWithMask*( zkey: ZKey, wtns: Witness, mask: Mask, pool: Taskpool, printTimings: bool): Proof = +proc generateProofWithMask*( zkey: ZKey, wtns: Witness, mask: Mask, pool: Taskpool, printTimings: bool): Proof {.raises: [ValueError].} = - # if (zkey.header.curve != wtns.curve): - # echo( "zkey.header.curve = " & ($zkey.header.curve) ) - # echo( "wtns.curve = " & ($wtns.curve ) ) - - assert( zkey.header.curve == wtns.curve ) + if zkey.header.curve != wtns.curve: + raise newException(ValueError, + "curve mismatch: zkey.header.curve=" & $zkey.header.curve & + " wtns.curve=" & $wtns.curve) let witness = wtns.values let hdr : GrothHeader = zkey.header let spec : SpecPoints = zkey.specPoints - let pts : ProverPoints = zkey.pPoints + let pts : ProverPoints = zkey.pPoints let nvars = hdr.nvars let npubs = hdr.npubs - assert( nvars == witness.len , "wrong witness length" ) + if nvars != witness.len: + raise newException(ValueError, + "wrong witness length: expected " & $nvars & + " (zkey.header.nvars), got " & $witness.len) # remark: with the special variable "1" we actuall have (npub+1) public IO variables var pubIO = newSeq[Fr[BN254_Snarks]]( npubs + 1) @@ -118,11 +120,11 @@ proc generateProofWithMask*( zkey: ZKey, wtns: Witness, mask: Mask, pool: Taskpo #------------------------------------------------------------------------------- -proc generateProofWithTrivialMask*( zkey: ZKey, wtns: Witness, pool: Taskpool, printTimings: bool ): Proof = +proc generateProofWithTrivialMask*( zkey: ZKey, wtns: Witness, pool: Taskpool, printTimings: bool ): Proof {.raises: [ValueError].} = let mask = Mask( r: zeroFr , s: zeroFr ) return generateProofWithMask( zkey, wtns, mask, pool, printTimings ) -proc generateProof*( zkey: ZKey, wtns: Witness, pool: Taskpool, printTimings = false ): Proof = +proc generateProof*( zkey: ZKey, wtns: Witness, pool: Taskpool, printTimings = false ): Proof {.raises: [ValueError].} = let mask = randomMask() return generateProofWithMask( zkey, wtns, mask, pool, printTimings ) diff --git a/groth16/verifier.nim b/groth16/verifier.nim index 091d34b..4ca41fe 100644 --- a/groth16/verifier.nim +++ b/groth16/verifier.nim @@ -29,13 +29,18 @@ from groth16/prover import Proof # the verifier # -proc verifyProof* (vkey: VKey, prf: Proof): bool = +proc verifyProof* (vkey: VKey, prf: Proof): bool {.raises: [ValueError].} = - assert( prf.curve == "bn128" ) + if prf.curve != "bn128": + raise newException(ValueError, + "unsupported curve: expected \"bn128\", got \"" & prf.curve & "\"") - assert( isInSubgroupG1(prf.pi_a) , "pi_a is not in G1" ) - assert( isInSubgroupG2(prf.pi_b) , "pi_b is not in G2" ) - assert( isInSubgroupG1(prf.pi_c) , "pi_c is not in G1" ) + if not isInSubgroupG1(prf.pi_a): + raise newException(ValueError, "pi_a is not in subgroup G1") + if not isInSubgroupG2(prf.pi_b): + raise newException(ValueError, "pi_b is not in subgroup G2") + if not isInSubgroupG1(prf.pi_c): + raise newException(ValueError, "pi_c is not in subgroup G1") var pubG1 : G1 = msmG1( prf.publicIO , vkey.vpoints.pointsIC ) diff --git a/tests/groth16/testProver.nim b/tests/groth16/testProver.nim index 1be5593..ed98fb2 100644 --- a/tests/groth16/testProver.nim +++ b/tests/groth16/testProver.nim @@ -1,11 +1,14 @@ -{.used.} +{.used.} import std/unittest import std/sequtils import taskpools +import constantine/math/io/io_fields +import constantine/named/properties_fields + import groth16/prover import groth16/verifier import groth16/fake_setup @@ -13,6 +16,7 @@ import groth16/zkey_types import groth16/files/witness import groth16/files/r1cs import groth16/bn128/fields +import groth16/bn128/curves #------------------------------------------------------------------------------- # simple hand-crafted arithmetic circuit @@ -71,11 +75,92 @@ proc testProof(zkey: ZKey, witness: Witness): bool = suite "prover": test "prove & verify simple multiplication circuit, `JensGroth` flavour": - let zkey = createFakeCircuitSetup( myR1cs, flavour=JensGroth ) + let zkey = createFakeCircuitSetup( myR1cs, flavour=JensGroth ) check testProof( zkey, myWitness ) test "prove & verify simple multiplication circuit, `Snarkjs` flavour": - let zkey = createFakeCircuitSetup( myR1cs, flavour=Snarkjs ) + let zkey = createFakeCircuitSetup( myR1cs, flavour=Snarkjs ) check testProof( zkey, myWitness ) #------------------------------------------------------------------------------- +# malformed-input handling: verifyProof must return `false`, never crash. + +# An on-curve but out-of-subgroup G2 point (same construction as in testCurve.nim). +const badPt2_x1 = fromHex(Fp[BN254_Snarks], "0x2") +const badPt2_xu = fromHex(Fp[BN254_Snarks], "0x0") +const badPt2_y1 = fromHex(Fp[BN254_Snarks], "0x181604d0560080401c08b557815482553e278257d98100d193a011c42782474d") +const badPt2_yu = fromHex(Fp[BN254_Snarks], "0x04f21f9d99cc25f694cf22ff70dc0ac4692e7a721b725dc454a217f04bd03e33") +let badPt2_x = mkFp2(badPt2_x1, badPt2_xu) +let badPt2_y = mkFp2(badPt2_y1, badPt2_yu) +let outOfSubgroupG2 = unsafeMkG2(badPt2_x, badPt2_y) + +# A G1 value with coordinates (1,1), which does NOT satisfy y^2 = x^3 + 3. +let bogusG1 = unsafeMkG1(oneFp, oneFp) + +proc buildGoodProof(): (VKey, Proof) = + var pool = Taskpool.new() + let zkey = createFakeCircuitSetup(myR1cs, flavour=Snarkjs) + let proof = generateProof(zkey, myWitness, pool) + let vkey = extractVKey(zkey) + pool.shutdown() + return (vkey, proof) + +suite "verifier input validation": + + test "wrong curve string raises ValueError": + let (vkey, proof) = buildGoodProof() + var bad = proof + bad.curve = "bls12-381" + expect ValueError: + discard verifyProof(vkey, bad) + + test "out-of-subgroup G2 pi_b raises ValueError": + let (vkey, proof) = buildGoodProof() + var bad = proof + bad.pi_b = outOfSubgroupG2 + expect ValueError: + discard verifyProof(vkey, bad) + + test "off-curve G1 pi_a raises ValueError": + let (vkey, proof) = buildGoodProof() + var bad = proof + bad.pi_a = bogusG1 + expect ValueError: + discard verifyProof(vkey, bad) + + test "off-curve G1 pi_c raises ValueError": + let (vkey, proof) = buildGoodProof() + var bad = proof + bad.pi_c = bogusG1 + expect ValueError: + discard verifyProof(vkey, bad) + + test "known-good proof still verifies": + let (vkey, proof) = buildGoodProof() + check verifyProof(vkey, proof) + +#------------------------------------------------------------------------------- +# prover input-validation: generateProof must raise ValueError (catchable in +# both --panics modes) for malformed inputs, not AssertionDefect. + +suite "prover input validation": + + test "curve mismatch between zkey and witness raises ValueError": + let zkey = createFakeCircuitSetup(myR1cs, flavour=Snarkjs) + var badWtns = myWitness + badWtns.curve = "bls12-381" + var pool = Taskpool.new() + expect ValueError: + discard generateProof(zkey, badWtns, pool) + pool.shutdown() + + test "wrong witness length raises ValueError": + let zkey = createFakeCircuitSetup(myR1cs, flavour=Snarkjs) + var badWtns = myWitness + badWtns.values = badWtns.values[0 ..< badWtns.values.len - 1] # drop one wire + var pool = Taskpool.new() + expect ValueError: + discard generateProof(zkey, badWtns, pool) + pool.shutdown() + +#------------------------------------------------------------------------------- From 66b45a046af3fe4a02d671d7ac1da6ddd74c4594 Mon Sep 17 00:00:00 2001 From: munna0908 Date: Tue, 9 Jun 2026 13:32:25 +0530 Subject: [PATCH 2/2] avoid returning execptions for invalid proof point --- groth16/verifier.nim | 9 +++------ tests/groth16/testProver.nim | 15 ++++++--------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/groth16/verifier.nim b/groth16/verifier.nim index 4ca41fe..e4ff150 100644 --- a/groth16/verifier.nim +++ b/groth16/verifier.nim @@ -35,12 +35,9 @@ proc verifyProof* (vkey: VKey, prf: Proof): bool {.raises: [ValueError].} = raise newException(ValueError, "unsupported curve: expected \"bn128\", got \"" & prf.curve & "\"") - if not isInSubgroupG1(prf.pi_a): - raise newException(ValueError, "pi_a is not in subgroup G1") - if not isInSubgroupG2(prf.pi_b): - raise newException(ValueError, "pi_b is not in subgroup G2") - if not isInSubgroupG1(prf.pi_c): - raise newException(ValueError, "pi_c is not in subgroup G1") + if not isInSubgroupG1(prf.pi_a): return false + if not isInSubgroupG2(prf.pi_b): return false + if not isInSubgroupG1(prf.pi_c): return false var pubG1 : G1 = msmG1( prf.publicIO , vkey.vpoints.pointsIC ) diff --git a/tests/groth16/testProver.nim b/tests/groth16/testProver.nim index ed98fb2..14a3a7e 100644 --- a/tests/groth16/testProver.nim +++ b/tests/groth16/testProver.nim @@ -114,26 +114,23 @@ suite "verifier input validation": expect ValueError: discard verifyProof(vkey, bad) - test "out-of-subgroup G2 pi_b raises ValueError": + test "out-of-subgroup G2 pi_b returns false": let (vkey, proof) = buildGoodProof() var bad = proof bad.pi_b = outOfSubgroupG2 - expect ValueError: - discard verifyProof(vkey, bad) + check (not verifyProof(vkey, bad)) - test "off-curve G1 pi_a raises ValueError": + test "off-curve G1 pi_a returns false": let (vkey, proof) = buildGoodProof() var bad = proof bad.pi_a = bogusG1 - expect ValueError: - discard verifyProof(vkey, bad) + check (not verifyProof(vkey, bad)) - test "off-curve G1 pi_c raises ValueError": + test "off-curve G1 pi_c returns false": let (vkey, proof) = buildGoodProof() var bad = proof bad.pi_c = bogusG1 - expect ValueError: - discard verifyProof(vkey, bad) + check (not verifyProof(vkey, bad)) test "known-good proof still verifies": let (vkey, proof) = buildGoodProof()