Function to export Groth16 proofs only works for proofs with exactly one commitment
Note regarding the deprecated target
Below we describe an issue in the zk-utils repository that was part of the originally contracted scope at the start of the audit. This repository was later deprecated and removed from the scope of the audit after we had found and reported the issue described below. We keep this finding here so that the report accurately reflects the findings we identified and reported during the auditing process. Due to the deprecation of the relevant code, no action is necessary in relation to this finding.
Description
In zk-utils, the function ExportProof
in common/utils/groth16_util.go is used to unpack gnark's Groth16 proofs, returning various fixed-size arrays. For the commitments, it returns commitment [2]*big.Int
, which is filled as follows:
commitment[0] = bn254Proof.Commitments[0].X.BigInt(new(big.Int))
commitment[1] = bn254Proof.Commitments[0].Y.BigInt(new(big.Int))
However, the type of bn254Proof.Commitments
is []curve.G1Affine
, and a gnark proof can indeed have zero, one, or more commitments.
Impact
The ExportProof
only works as intended for proofs that have exactly one commitment.
If instead there is no commitment, then the call will crash, due to an of out-of-bounds array access. Indeed, this is what happens when running the zk-bridge program circuits/fabric/headers/main/main.go. The circuit in this case has no commitment, and running it results in output like the following:
chunk root ddc08833ebef5364d5cdedc989770becc949caf6cbdc72bba0e842d442136c06
prev hash 0301010101010101010101010101010101010101010101010101010101010102
end hash 931cf1c54d8ab666b200f2f88748a6a6ec03d3795a3a453895df015b2013b96d
chunk root ddc08833ebef5364d5cdedc989770becc949caf6cbdc72bba0e842d442136c06
prev hash 0301010101010101010101010101010101010101010101010101010101010102
end hash 931cf1c54d8ab666b200f2f88748a6a6ec03d3795a3a453895df015b2013b96d
[ddc08833ebef5364d5cdedc989770bec c949caf6cbdc72bba0e842d442136c06]
[3010101010101010101010101010101 1010101010101010101010101010102]
[931cf1c54d8ab666b200f2f88748a6a6 ec03d3795a3a453895df015b2013b96d]
1
4
compile
12:48:23 INF compiling circuit
12:48:23 INF parsed circuit inputs nbPublic=8 nbSecret=21764
12:48:53 INF building constraint builder nbConstraints=4502728
setup
gen witness
prove
12:55:30 DBG constraint system solver done nbConstraints=4502728 took=1415.957874
12:55:49 DBG prover done acceleration=none backend=groth16 curve=bn254 nbConstraints=4502728 took=18987.907852
panic: runtime error: index out of range [0] with length 0
goroutine 1 [running]:
github.com/brevis-network/zk-utils/common/utils.ExportProof({0xdc2c40?, 0xc000592000})
/home/user/go/pkg/mod/github.com/brevis-network/zk-utils@v0.0.0-20240508102248-74fa43bfe5f5/common/utils/groth16_util.go:25 +0x292
main.main()
/home/user/brevis-network-zk-bridge/circuits/fabric/headers/main/main.go:53 +0x5f8
exit status 2
Should the proof have more than one commitment, the ExportProof
function will fail to return sufficient information to reconstruct the original proof, as the commitments after the first one will be missing. Should verification be attempted from the return values of ExportProof
in that case, the proof will fail to verify.
Recommendations
We recommend to change ExportProof
to support an arbitrary number of commitments. The return variable commitment [2]*big.Int
would need to be changed to, for example, commitments [][2]*big.Int
.
Remediation
The code related to the finding was deprecated before completion of the audit.