Skip to main content
Try out the RareSkills ZK AI Tutor!
The RareSkills Book of Zero Knowledge

Groth16 Explained

Last updated on Mar 16, 2026

The Groth16 algorithm enables a quadratic arithmetic program to be computed by a prover over elliptic curve points derived in a trusted setup, and quickly checked by a verifier. It uses auxiliary elliptic curve points from the trusted setup to prevent forged proofs.

Prerequisites

This article is a chapter in the RareSkills Book of Zero Knowledge Proofs. It assumes you are familiar with the prior chapters.

Notation

We refer to an elliptic curve point belonging to the 𝔾1 elliptic curve group as [x]1 and an elliptic curve point belonging to the 𝔾2 elliptic curve group as [x]2. A pairing between [x]1 and [x]2 is denoted as [x]1∙[x]2 and produces an element in 𝔾12. Variables in bold such as 𝐚 are vectors, upper case bold letters such as 𝐋 are matrices, and field elements (sometimes informally referred to as “scalars”) are lower case letters such as d. All arithmetic operations happen in a finite field with a characteristic that equals the order of the elliptic curve group.

Given an Arithmetic Circuit (ZK Circuit), we convert it to a Rank 1 Constraint System (R1CS) 𝐋𝐚∘𝐑𝐚=𝐎𝐚 with matrices of dimension n rows and m columns with a witness vector 𝐚. Then, we can convert the R1CS to Quadratic Arithmetic Program (QAP) by interpolating the columns of the matrices as y values over the x values [1,2,...,n]. Since 𝐋, 𝐑, and 𝐎 have m columns, we will end up with three sets of m polynomials:

u1(x),...,um(x)m polynomials interpolated on the m columns of 𝐋v1(x),...,vm(x)m polynomials interpolated on the m columns of 𝐑w1(x),...,wm(x)m polynomials interpolated on the m columns of 𝐎

From this, we can construct a Quadratic Arithmetic Program (QAP):

∑i=1maiui(x)∑i=1maivi(x)=∑i=1maiwi(x)+h(x)t(x)

where

t(x)=(x−1)(x−2)…(x−n)

and
h(x)=∑i=1maiui(x)∑i=1maivi(x)−∑i=1maiwi(x)t(x)

If a third party creates a structured reference string (srs) via a powers of tau ceremony, then the prover can evaluate sum terms (the ∑aifi(x) terms) in the QAP at a hidden point τ. Let the structured reference strings be computed as follows:

[Ωn−1,Ωn−2,…,Ω2,Ω1,G1]=[τnG1,τn−1G1,…,τG1,G1]srs for G1[Θn−1,Θn−2,…,Θ2,Θ1,G2]=[τnG2,τn−1G2,…,τG2,G2]srs for G2[Υn−2,Υn−3,…,Υ1,Υ0]=[τn−2t(τ)G1,τn−3t(τ)G1,…,τt(τ)G1,t(τ)G1]srs for h(τ)t(τ)

We refer to f(τ) as a polynomial evaluated on a structured reference string [τdG1,...,τ2G1,τG1,G1] via the inner product:

f(τ)=∑i=1dfiΩi=⟨[fd,fd−1,...,f1,f0],[Ωd,Ωd−1,...,G1]⟩

or for 𝔾2 srs:

f(τ)=∑i=1dfiΘi=⟨[fd,fd−1,...,f1,f0],[Θd,Θd−1,...,G2]⟩

f(τ) is shorthand for the above expression, and produces an elliptic curve point. It does not mean the prover knows τ.

The prover can evaluate their QAP on the trusted setup by computing:

[A]1=∑i=1maiui(τ)[B]2=∑i=1maivi(τ)[C]1=∑i=1maiwi(τ)+h(τ)t(τ)

The details of this computation are discussed in our tutorial Quadratic Arithmetic Programs over Elliptic Curves.

If the QAP is balanced, then the following equation holds:

[A]1∙[B]2=?[C]1∙G2

Motivation

Simply presenting ([A]1,[B]2,[C]1) is not a convincing argument that the prover knows 𝐚 such that the QAP is balanced.

The prover can simply invent values a, b, c where ab=c, compute

[A]1=aG1[B]2=bG2[C]1=cG1

and present those as elliptic curve points [A]1, [B]2, [C]1 to the verifier.

Thus, the verifier has no idea if ([A]1,[B]2,[C]1) were the result of a satisfied QAP or not.

We need to force the prover to be honest without introducing too much computational overhead. The first algorithm to accomplish this was “Pinocchio: Nearly Practical Verifiable Computation.” This was usable enough for ZCash to base the first version of their blockchain on it.

However, Groth16 was able to accomplish the same thing in much fewer steps, and the algorithm is still widely in use today, as no algorithm since has produced as efficient an algorithm for the verification step (though other algorithms have eliminated the trusted setup or significantly reduced the amount of work for the prover).

Update for 2024: A paper rather triumphantly titled “Polymath: Groth16 is not the limit” published in Cryptology demonstrates an algorithm that requires fewer verifier steps than Groth16. However, there are no known implementations of the algorithm at this time of writing.

Preventing forgery Part 1: Introducing α and β

An “unsolveable” verification formula

Suppose we update our verification formula to the following:

[A]1∙[B]2=?[D]12+[C]1∙G2

Note that we are using additive notation for the G12 group for convenience.

Here, [D]12 is an element from G12 and has an unknown discrete logarithm.

We now show that it is impossible for a verifier to provide a solution ([A]1,[B]2,[C]1) to this equation, without knowing the discrete logarithm of [D]12.

Attack 1: Forging A and B and deriving C

Suppose the prover randomly selects a’ and b’ to produce [A]1 and [B]2 and tries to derive a value [C’] that is compatible with the verifier’s formula.

[A]1∙[B]2=?[D]12+[C]1∙G2

Knowing the discrete logarithms of [A]1 and [B]2, the malicious prover tries to solve for [C’] by doing

[A]1∙[B]2−[D]12⏟χ12=[C′]1∙G2[χ]12=[C′]1∙G2

The final line is requires the prover to solve for the discrete log of χ12, so they cannot compute a valid discrete log for [C′]1.

Attack 2: Forging C and deriving A and B

Here the prover picks a random point c′ and computes [C′]1. Because they know c′, they can try to discover a compatible combination of a′ and b′ such that

[A]1∙[B]2=?[D]12+[C]1∙G2⏟[ζ]12[A]1∙[B]2=?[ζ]12

This requires the prover, given [ζ]12, to come up with an [A]1 and [B]2 that pair to produce [ζ]12.

Similar to the discrete log problem, we rely on unproven cryptographic assumptions that this computation (decomposing an element in 𝔾12 into a 𝔾1 and 𝔾2 element) is infeasible. In this case, the assumption that we cannot decompose [ζ]12 into [A]1 and [B]2 is called the Bilinear Diffie-Hellman Assumption. The interested reader can see a related discussion on the Decisional Diffie-Hellman Assumption.

(Unproven does not mean unreliable. If you can find a way to prove or disprove this assumption, fame and fortune awaits you! In practice, there is no known way to decompose [ζ]12 into [A]1 and [B]2 and it is believed to be computationally infeasible.)

How α and β are used

In practice, Groth16 doesn’t use a term [D]12. Instead, the trusted setup generates two random scalars α and β and publishes the elliptic curve points ([α]1,[β]2) computed as:

[α]1=αG1[β]2=βG2

What we referred to as [D]12 is simply [α]1∙[β]2.

Re-deriving the proving and verification formulas

To make the verification formula [A]1∙[B]2=?[α]1∙[β]2+[C]1∙G2 “solvable”, we need to alter our QAP formula to incorporate α and β.

∑i=1maiui(x)∑i=1maivi(x)=∑i=1maiwi(x)+h(x)t(x)

Now consider what happens if we introduce terms θ and η to the left hand side of the equation:

(θ+∑i=1maiui(x))(η+∑i=1maivi(x))=
=θη+θ∑i=1maivi(x)+η∑i=1maiui(x)+∑i=1maiui(x)∑i=1maivi(x)

We can substitute the rightmost terms using the original QAP definition:

=θη+θ∑i=1maivi(x)+η∑i=1maiui(x)+∑i=1maiui(x)∑i=1maivi(x)

=θη+θ∑i=1maivi(x)+η∑i=1maiui(x)+∑i=1maiwi(x)+h(x)t(x)

Now we can introduce an “expanded” QAP with the following definition:

(θ+∑i=1maiui(x))(η+∑i=1maivi(x))=θη+θ∑i=1maivi(x)+η∑i=1maiui(x)+∑i=1maiwi(x)+h(x)t(x)

As a sneak peak to where we are going, if we replace θ with [α]1 and η with [β]2, we get updated verification formula from earlier:

[A]1∙[B]2=?[α]1∙[β]2+[C]1∙G2

where

([α]1+∑i=1maiui(τ))⏟[A]1([β]2+∑i=1maivi(τ))⏟[B]2=[α]1∙[β]2+(α∑i=1maivi(τ)+β∑i=1maiui(τ)+∑i=1maiwi(τ)+h(τ)t(τ))⏟[C]1∙G2

The prover can compute [A]1 and [B]2 without knowing τ, α, or β. Given the structured reference string (powers of τ) and the elliptic curve points ([α]1,[β]2), the prover computes [A]1 and [B]2 as

[A]1=[α]1+∑i=1maiui(τ)[B]2=[β]2+∑i=1maivi(τ)

Here, aiui(τ) does not mean the prover knows τ. The prover is using the structure reference string [τn−1G1,τn−2G1,…,τG1,G1] to compute ui(τ) for i=1,2,…,m and the G2 srs for for [B]2.

However, it isn’t currently possible to compute [C]1 without knowing α and β. The prover cannot pair [α]1 with ∑aiui(τ) and [β]2 with ∑aivi(τ) because that would create a 𝔾12 point, whereas the prover needs a 𝔾1 point for [C]1.

Instead, the trusted setup needs to precompute m polynomials for the problematic C term of the expanded QAP.

α∑i=1maivi(τ)+β∑i=1maiui(τ)+∑i=1maiwi(τ)

With some algebraic manipulation, we combine the sum terms into a single sum:

=∑i=1m(αaivi(τ)+βaiui(τ)+aiwi(τ))

and factor out ai:

=∑i=1mai(αvi(τ)+βui(τ)+wi(τ))

The trusted setup can create m polynomials evaluated at τ from the boxed term above, and the prover can use that to compute the sum. The exact details are shown in the next section.

Summary of the algorithm so far

Trusted setup steps

Concretely, the trusted setup computes the following:

α,β,τ←random scalars[τn−1G1,τn−2G1,…,τG1,G1]←srs for 𝔾1[τn−1G2,τn−2G2,…,τG2,G2]←srs for 𝔾2[τn−2t(τ)G1,τn−3t(τ)G1,…,τt(τ)G1,t(τ)G1]←srs for h(τ)t(τ)[Ψ1]1=(αv1(τ)+βu1(τ)+w1(τ))G1[Ψ2]1=(αv2(τ)+βu2(τ)+w2(τ))G1⋮[Ψm]1=(αvm(τ)+βum(τ)+wm(τ))G1

The trusted setup publishes

([α]1,[β]2,srsG1,srsG2,srs for h(τ)t(τ),[Ψ1]1,[Ψ2]1,…,[Ψm]1)

Prover steps

The prover computes

[A]1=[α]1+∑i=1maiui(τ)[B]2=[β]2+∑i=1maivi(τ)[C]1=∑i=1mai[Ψi]1+h(τ)t(τ)

Note that we replaced the “problematic” polynomial

=∑i=1mai(αvi(τ)+βui(τ)+wi(τ))

(the one that contained α and β) with

∑i=1mai[Ψi]1

Verifier steps

The verifier computes:

[A]1∙[B]2=?[α]1∙[β]2+[C]1∙G2

Supporting public inputs

The verifier formula so far does not support public inputs, i.e. making a portion of the witness public.

By convention, public portions of the witness are the first ℓ elements of the vector 𝐚. To make those elements public, the prover simply reveals them:

[a1,a2,…,aℓ]

For the verifier to test that those values were in fact used, verifier must carry out some of the computation that the prover was originally doing.

Specifically, the prover computes:

[A]1=[α]1+∑i=1maiui(τ)[B]2=[β]2+∑i=1maivi(τ)[C]1=∑i=ℓ+1mai[Ψi]1+h(τ)t(τ)

Note that only the computation of [C]1 changed – the prover only uses the ai and Ψi terms ℓ+1 to m.

The verifier computes the first ℓ terms of the sum:

[X]1=∑i=1ℓaiΨi

And the verification equation is:

[A]1∙[B]2=?[α]1∙[β]2+[X]1∙G2+[C]1∙G2

Part 2: Separating the public inputs from the private inputs with γ or δ

Forging proofs by misuing Ψi for i≤ℓ

The assumption in the equation above is that the prover is only using Ψℓ+1 to Ψm to compute [C]1, but nothing stops a dishonest prover from using Ψ1 to Ψℓ to compute [C]1, leading to a forged proof.

For example, here is our current verification equation:

[A]1∙[B]2=?[α]1∙[β]2+∑i=1ℓaiΨi+[C]1∙G2

If we expand the C term under the hood, we get the following:

[A]1∙[B]2=?[α]1∙[β]2+∑i=1ℓaiΨi+(∑i=ℓ+1mai[Ψi]1+h(τ)t(τ))⏟C∙G2

Suppose for example and without loss of generality that 𝐚=[1,2,3,4,5] and ℓ=3. In that case, the public part of the witness is [1,2,3] and the private part is [4,5].

The final equation would be as follows:

[A]1∙[B]2=?[α]1∙[β]2+(1Ψ1+2Ψ2+3Ψ3)∙G2+(4Ψ4+5Ψ5+h(τ)t(τ))⏟C∙G2

However, nothing stops the prover from creating an valid portion of the public witness as [1,2,0] and moving the zeroed out public portion to the private part of the computation as follows:

[A]1∙[B]2=?[α]1∙[β]2+(1Ψ1+2Ψ2+0Ψ3)∙G2+(3Ψ3+4Ψ4+5Ψ5+h(τ)t(τ))⏟C∙G2

The equation above is valid, but the witness does not necessarily satisfy the original constraints.

Therefore, we need to prevent the prover from using Ψ1 to Ψℓ as part of the computation of [C]1.

Introducing γ and/or δ

To avoid the problem above, the trusted setup introduces a new scalar : γ or δ to force Ψℓ+1 to Ψm to be separate from Ψ1 to Ψℓ. To do this, the trusted setup divides (multiplies by the modular inverse) the private terms (that constitute [C]1) by δ and/or the public terms (that constitute [X]1, the sum the verifier computes) by γ.

Since the h(τ)t(τ) term is embedded in [C]1, those terms also need to be divided by δ. If either δ and γ have an unknown discrete logarithm, then the forgery described earlier along possible other methods are avoided. This method was used in Zcash’s Sapling’s‑based trusted setups where γ is simply left to G2 and δ is still updated from G2 to a random value at later trusted setup stages.

α,β,τ,γ,δ←random scalars[τn−1G1,τn−2G1,…,τG1,G1]←srs for 𝔾1[τn−1G2,τn−2G2,…,τG2,G2]←srs for 𝔾2[τn−2t(τ)δG1,τn−3t(τ)δG1,…,τt(τ)δG1,t(τ)δG1]←srs for h(τ)t(τ)public portion of the witness[Ψ1]1=αv1(τ)+βu1(τ)+w1(τ)γG1[Ψ2]1=αv2(τ)+βu2(τ)+w2(τ)γG1⋮[Ψℓ]1=αvℓ(τ)+βuℓ(τ)+wℓ(τ)γG1private portion of the witness[Ψℓ+1]1=αvℓ+1(τ)+βuℓ+1(τ)+wℓ+1(τ)δG1[Ψℓ+2]1=αvℓ+2(τ)+βuℓ+2(τ)+wℓ+2(τ)δG1⋮[Ψm]1=αvm(τ)+βum(τ)+wm(τ)δG1

The trusted setup publishes

([α]1,[β]2,[γ]2,[δ]2,srsG1,srsG2,srs for h(τ)t(τ),[Ψ1]1,[Ψ2]1,…,[Ψm]1)

The prover steps are the same as before:

[A]1=[α]1+∑i=1maiui(τ)[B]2=[β]2+∑i=1maivi(τ)[C]1=∑i=ℓ+1mai[Ψi]1+h(τ)t(τ)

And the verifier steps now include pairing by [γ]2 and/or [δ]2 to cancel out the denominators:

[A]1∙[B]2=?[α]1∙[β]2+[X]1∙[γ]2+[C]1∙[δ]2

Part 3: Enforcing true zero knowledge: r and s

Our scheme is not yet truly zero knowledge. If an attacker is able to guess our witness vector (which is possible if there is only a small range of valid inputs, e.g. secret voting from privileged addresses), then they can verify their guess is correct by comparing their constructed proof to the original proof.

As a trivial example, suppose our claim is x1 and x2 are both either 0 or 1. The corresponding arithmetic circuit would be

x1(x1−1)=0x2(x2−1)=0

An attacker only needs to guess four combinations to figure out what the witness is. That is, they guess a witness, generate a proof, and see if their answer matches the original proof.

To prevent guessing, the prover needs to “salt” their proof, and the verification equation needs to be modified to accommodate the salt.

The prover samples two random field elements r and s and adds them to A and B to make the witness unguessable – an attacker would have to guess both the witness and the salts r and s:

[A]1=[α]1+∑i=1maiui(τ)+r[δ]1[B]2=[β]2+∑i=1maivi(τ)+s[δ]2[B]1=[β]1+∑i=1maivi(τ)+s[δ]1[C]1=∑i=ℓ+1mai[Ψi]1+h(τ)t(τ)+As+Br−rs[δ]1

To derive the final verification formula, let’s temporarily ignore that we don’t know the discrete logs of the Greek letter terms and compute the left-hand-side of the verification equation AB:

(α+∑i=1maiui(x)+rδ)⏟A(β+∑i=1maivi(x)+sδ)⏟B

Expanding the terms we get:

αβ+α∑i=1maivi(x)+αsδ+β∑i=1maiui(x)+∑i=1maiui(x)∑i=1maivi(x)+∑i=1maiui(x)sδ+rδβ+rδ∑i=1maivi(x)+rδsδ

We can select out the original terms for C

αβ+α∑i=1maivi(x)+αsδ+β∑i=1maiui(x)+∑i=1maiui(x)∑i=1maivi(x)+∑i=1maiui(x)sδ+rδβ+rδ∑i=1maivi(x)+rδsδ

And combine them on the left, leaving the new terms on the right:

αβ+α∑i=1maivi(x)+β∑i=1maiui(x)+∑i=1maiui(x)∑i=1maivi(x)+αsδ+∑i=1maiui(x)sδ+rδβ+rδ∑i=1maivi(x)+rδsδ

We further rearrange the underlined terms to write them in terms of Asδ and Brδ as follows. We also split rδsδ into rsδ2+rsδ2−rsδ2:

=αsδ+∑i=1maiui(x)sδ+rsδ2+rδβ+rδ∑i=1maivi(x)+rsδ2−rsδ2

Group the s and r terms together:

=(αsδ+∑i=1maiui(x)sδ+rsδ2)+(rδβ+rδ∑i=1maivi(x)+rsδ2)−rsδ2

Factor out sδ and rδ:

=(α+∑i=1maiui(x)+rδ)sδ⏟Asδ+(β+∑i=1maivi(x)+sδ)rδ⏟Brδ−rsδ2

Substitute A and B:

=Asδ+Brδ−rsδ2

So our final equation is

(α+∑i=1maiui(x)+rδ)(β+∑i=1maivi(x)+sδ)=αβ+∑i=1mai(αvi(x)+βui(x)+wi(x))+h(x)t(x)+Asδ+Brδ−rsδ2

We now break it into the public and private portions:

(α+∑i=1maiui(x)+rδ)(β+∑i=1maivi(x)+sδ)=αβ+∑i=1ℓai(αvi(x)+βui(x)+wi(x))⏟public+∑i=ℓ+1mai(αvi(x)+βui(x)+wi(x))+h(x)t(x)+Asδ+Brδ−rsδ2⏟private

We want the public portion and the private portion to be separated by γ and δ respectively:

(α+∑i=1maiui(x)+rδ)(β+∑i=1maivi(x)+sδ)=αβ+γ∑i=1ℓai(αvi(x)+βui(x)+wi(x))γ+δ∑i=ℓ+1mai(αvi(x)+βui(x)+wi(x))+h(x)t(x)δ+Asδ+Brδ−rsδ2

δ cancels for some of the terms:

(α+∑i=1maiui(x)+rδ)(β+∑i=1maivi(x)+sδ)=αβ+γ∑i=1ℓai(αvi(x)+βui(x)+wi(x))γ+δ(∑i=ℓ+1mai(αvi(x)+βui(x)+wi(x))+h(x)t(x)δ+As+Br−rsδ)

We now separate this equation in to the verifier and prover portions. The boxed terms are the verifier portion, the underbrace terms are the terms that the prover provides:

(α+∑i=1maiui(x)+rδ)⏟[A]1(β+∑i=1maivi(x)+sδ)⏟[B]2=αβ+γ∑i=1ℓai(αvi(x)+βui(x)+wi(x))γ+δ(∑i=ℓ+1mai(αvi(x)+βui(x)+wi(x))+h(x)t(x)δ+As+Br−rsδ)⏟[C]1

Groth16 Proof Algorithm

We are now ready to show the Groth16 algorithm end-to-end. The trusted setup and the verification steps remain unchanged from the previous example where we incorporated γ and δ. Only the prover’s calculation changes to incorporate r and s.

Trusted Setup

α,β,τ,γ,δ←random scalars[τn−1G1,τn−2G1,…,τG1,G1]←srs for 𝔾1[τn−1G2,τn−2G2,…,τG2,G2]←srs for 𝔾2[τn−2t(τ)δG1,τn−3t(τ)δG1,…,τt(τ)δG1,t(τ)δG1]←srs for h(τ)t(τ)public portion of the witness[Ψ1]1=αv1(τ)+βu1(τ)+w1(τ)γG1[Ψ2]1=αv2(τ)+βu2(τ)+w2(τ)γG1⋮[Ψℓ]1=αvℓ(τ)+βuℓ(τ)+wℓ(τ)γG1private portion of the witness[Ψℓ+1]1=αvℓ+1(τ)+βuℓ+1(τ)+wℓ+1(τ)δG1[Ψℓ+2]1=αvℓ+2(τ)+βuℓ+2(τ)+wℓ+2(τ)δG1⋮[Ψm]1=αvm(τ)+βum(τ)+wm(τ)δG1

The trusted setup publishes

([α]1,[β]1[β]2,[γ]2,[δ]1[δ]2,srsG1,srsG2,srs for h(τ)t(τ),[Ψ1]1,[Ψ2]1,…,[Ψm]1)

Prover Steps

Prover has a witness 𝐚 and generates random scalars r and s.

[A]1=[α]1+∑i=1maiui(τ)+r[δ]1[B]1=[β]1+∑i=1maivi(τ)+s[δ]1[B]2=[β]2+∑i=1maivi(τ)+s[δ]2[C]1=∑i=ℓ+1mai[Ψi]1+h(τ)t(τ)+[A]1s+[B]1r−rs[δ]1

The prover publishes ([A]1,[B]2,[C]1,[a1,...,aℓ]).

Verifier Steps

The verifier checks

[X]1=∑i=1ℓaiΨi[A]1∙[B]2=?[α]1∙[β]2+[X]1∙[γ]2+[C]1∙[δ]2

Verifying Groth16 in Solidity

At this point, you have sufficient knowledge to understand the proof verification code in Solidity. Here is Tornado Cash’s proof verification code. The reader is encouraged to read the source code closely. If the reader is comfortable with Solidity assembly programming, then understanding this source code will not be difficult as the variable names are consistent with the ones in this article.

There is also library support for Groth16 on Solana.

Security Issues to Be Aware Of

Groth16 is Malleable

Groth16 proofs are malleable. Given a valid proof

([A]1,[B]2,[C]1), an attacker can compute the point negation of [A]1 and [B]2 and present a new proof as ([A′]1,[B′]2,[C]1) where [A′]1=𝗇𝖾𝗀([A]1) and [B′]2=𝗇𝖾𝗀([B]2).

To see that [A]1∙[B]2=[A′]1∙[B′]2, consider the following code:

from py_ecc.bn128 import G1, G2, multiply, neg, eq, pairing

# chosen arbitrarily
x = 10
y = 100
A = multiply(G1, x)
B = multiply(G2, y)

A_p = neg(A)
B_p = neg(B)

assert eq(pairing(B, A), pairing(B_p, A_p))

Intuitively, the attacker is multiplying A and B by −1, and (−1)×(−1) cancels itself out in the pairing.

Hence, if the verification formula accepts

[A]1∙[B]2=?[α]1∙[β]2+[X]1∙[γ]2+[C]1∙[δ]2

then it will also accept

𝗇𝖾𝗀([A]1)∙𝗇𝖾𝗀([B]2)=?[α]1∙[β]2+[X]1∙[γ]2+[C]1∙[δ]2

The defense against this attack is described in the following section.

You can see a proof of concept of this attack in this article.

The prover can create an unlimited number of proofs for the same witness

This isn’t a “security issue” per se – it is necessary to achieve Zero Knowledge. However, the application needs a mechanism to track which facts have already been proven and cannot rely on the uniqueness of the proof to achieve that.

Learn more with RareSkills

Our ability to publish material like this free of charge depends on the continued support of our students. Consider signing up for our Zero Knowledge Bootcamp, Web3 Bootcamps, or getting a job on RareTalent.

Originally Published August 31, 2023