Simulating Bulletproofs Under Adaptive Corruptions

·26 min read
Zero-Knowledge ProofsBulletproofsAdaptive SecuritySecure ErasuresPedersen Commitments
Abstract

In this post, we show how to reconstruct the state of an honest prover given a simulated Bulletproof and a valid witness for the proof’s statement. Protocols that use Bulletproofs can therefore use our technique to handle adaptive corruptions without assuming secure erasures for the randomness of these proofs.

A zero-knowledge proof [GMR85] allows a prover to convince a verifier that a public statement x\stmt of an NP-relation is true without revealing the corresponding witness w\wit. For example, x\stmt may be a public key x=gw\stmt=g^\wit, where gg generates a group of prime order pp and w\wit is the secret key. A zero-knowledge proof could then convince the verifier that the prover knows the secret key, without revealing any information besides the already known public key x\stmt. In this post, we focus on non-interactive zero-knowledge proofs, or NIZKs [BFM88], which allow the prover to generate such a proof π\pi without interacting with the verifier.

In cryptography, we formalize the promise that a proof “reveals no additional information” through simulation. We say a proof is zero-knowledge if an efficient algorithm (called a simulator) that only knows a statement x\stmt but does not know the witness w\wit can produce valid proofs that no efficient verifier can distinguish from honest proofs. Whatever the verifier learns from an honest proof, it could then also have learned from a simulated proof, which carries no information about the witness to begin with.

At first glance, this seems paradoxical. If a simulator can produce valid proofs without knowing the witness, why should a proof convince a verifier? The simulator has access to a trapdoor, a piece of secret information that lets it produce proofs without the witness. At the same time, no real prover has access to the trapdoor, so proofs from a real prover are sound (can only be computed when knowing a valid witness). For Fiat–Shamir-based proofs [FS86], which are the subject of this post, the simulator’s trapdoor is the power to program the random oracle [BR93], which is not possible with a real hash function.

This simulation property is useful when proving the security of a larger protocol, where NIZKs are used as a building block. Throughout the security proof of such a higher-level protocol, the reduction can first use the NIZK’s simulator to generate proofs without knowing a valid witness, and then embed a challenge element in the NIZK’s statement. With our example NIZK of secret-key knowledge, this would allow the reduction to embed a discrete-logarithm (DLog) challenge element into the public key while still producing accepting proofs of knowledge of the secret key. If the adversary breaks the modified protocol, the reduction uses this break to compute the discrete logarithm of the embedded element. Since replacing honest proofs with simulated ones results in an indistinguishable view for the adversary, any adversary that breaks the real protocol can also be used to break the modified protocol, which in turn allows solving DLog. Because DLog is assumed to be hard, no such adversary can exist.

Three security-proof experiments compared from left to right. Exp0 executes the protocol honestly, Exp1 simulates its non-interactive zero-knowledge proofs, and Exp2 embeds a hard-problem challenge in a public statement.
Figure 1: Experiments in the security proof. Consecutive experiments are computationally indistinguishable.

This proof technique is standard in cryptographic literature and yields secure higher-level protocols under static corruptions, where the adversary chooses which parties to corrupt before the protocol starts. Under static corruptions, the reduction only needs to simulate the remaining honest parties. These parties stay uncorrupted throughout the execution, so the reduction never has to reveal the internal state behind their simulated proofs. For this part of the security argument, producing indistinguishable proofs is sufficient.

In practice, however, adversaries do not usually decide before a protocol starts which parties they will eventually corrupt. To model this, cryptographers use adaptive corruptions [CGJKR99], where the adversary can choose parties to corrupt during the execution of a protocol, based on the protocol messages it has already seen. If the adversary decides to corrupt such a party, then the reduction has to provide all secret information in the state of this party (including all the randomness used so far). The gap between static and adaptive corruptions might seem small at first sight, but adaptive corruptions have a big impact on the way we can do security proofs. For example, an adversary could wait until all honest parties have published a NIZK, and then decide to corrupt a random party.

Even if this does not help the adversary break the protocol, it complicates our proof. If we want to use the proof strategy discussed above, we cannot simply replace all honest proofs by simulated ones, since the reduction has to reveal the randomness behind a simulated proof as soon as its prover is corrupted. The adversary can check that this randomness is consistent with the public transcript by running the honest prover again. A simulated proof, however, was produced without the witness, and the random choices of the simulator need not be valid randomness for the honest prover. Even if the witness becomes available upon corruption, the proof is already fixed and cannot be replaced. Thus, indistinguishability of the proof alone no longer suffices.

Three security-proof experiments under adaptive corruption. In the experiment with simulated proofs, a corruption asks for the prover’s retained randomness, which must reproduce the published proof.
Figure 2: Experiments under adaptive corruption. The corruption asks for the state behind a proof that was simulated without a witness.

There are three ways around this problem. First, the reduction can guess in advance which parties the adversary will corrupt. It then computes the proofs of these parties honestly (it knows their witnesses, since it embeds the challenge only in the statements of the others) and simulates the remaining proofs. If the guess turns out to be wrong, the reduction aborts. For a single corruption among nn parties, this costs a factor of nn in the success probability of the reduction, which is still polynomial. Threshold protocols, however, allow the adversary to corrupt up to tt of the nn parties, and if it corrupts tt of them at random, a guess of this set is correct only with probability 1/(nt)1/\binom{n}{t}. For n=100n=100 and t=50t=50, the reduction loses a factor larger than 2962^{96} in its success probability.

Second, honest parties can use secure erasures [CGJKR99] and delete the randomness of a proof directly after sending it. A later corruption then no longer reveals this randomness. This, however, is an additional assumption on how honest parties are implemented.

Third, the reduction can simulate all proofs anyway and, once the witness becomes available upon corruption, try to reconstruct randomness that makes the honest prover output exactly the proof it already published. For this post we can simply assume that the reduction obtains a valid witness for the statement of a corrupted party, since upon corruption the reduction must hand over the witness as part of the party’s state anyway.

Reconstructing the prover’s state#

We take the third approach, since we want to avoid assuming secure erasures and cannot efficiently guess which parties the adversary will corrupt. This means that we want to reconstruct randomness for a simulated proof given a valid witness.

If a reduction is able to reconstruct the prover’s state behind a simulated proof, this fixes the gap in the sketched adaptive security proof of the higher-level protocol. Throughout the security proof of the higher-level protocol, the reduction can again first use the NIZK’s simulator to generate proofs without knowing a valid witness, and then embed a challenge element in the NIZK’s statement. When the adversary corrupts a party, the reduction obtains this party’s witness, uses it to reconstruct the randomness behind every proof the party has published, and returns this randomness as part of the party’s internal state.

A simulator first publishes a proof without a witness, then reconstructs consistent randomness after a valid witness becomes available upon corruption.
Figure 3: The proof is fixed before the witness is known. The reconstructed randomness reproduces it with the later witness.

To make this simulation strategy undetectable to the adversary, the reconstructed randomness has to satisfy two requirements. It must make the honest prover reproduce exactly the proof that was published, and it must be distributed like the randomness of an honest prover.

We formalize this property as honest prover state reconstruction, following Groth, Ostrovsky and Sahai [GOS06].

Definition 1 (Honest prover state reconstruction).

Let Π=(Prove,Verify)\Pi=(\mathrm{Prove},\mathrm{Verify}) be a NIZK for a relation R\mathcal R in the random oracle model, where Prove\mathrm{Prove} takes its randomness rr as an explicit input and every public parameter is derived from the random oracle HH. Then Π\Pi has honest prover state reconstruction if there are efficient algorithms Sim\mathrm{Sim} and Rec\mathrm{Rec} with a shared state st\mathrm{st} such that for every efficient adversary A\mathcal A

Pr⁡[AH, Oreal=1]≈Pr⁡[ASim, Osim=1], \Pr\bigl[\mathcal A^{H,\,\mathcal O_{\mathrm{real}}}=1\bigr] \approx \Pr\bigl[\mathcal A^{\mathrm{Sim},\,\mathcal O_{\mathrm{sim}}}=1\bigr],

where HH is a random function in the first experiment and Sim\mathrm{Sim} answers the adversary’s random-oracle queries in the second. Both oracles take a pair (x,w)(\stmt,\wit), return ⊥\bot if (x,w)∉R(\stmt,\wit)\notin\mathcal R, and otherwise answer as follows.

  • Oreal(x,w)\mathcal O_{\mathrm{real}}(\stmt,\wit) samples rr, computes π←ProveH(x,w;r)\pi\gets\mathrm{Prove}^{H}(\stmt,\wit;r), and returns (π,r)(\pi,r).
  • Osim(x,w)\mathcal O_{\mathrm{sim}}(\stmt,\wit) lets Sim\mathrm{Sim} compute a proof π\pi on input x\stmt, programming its random oracle as needed, then computes r←Rec(st,x,w)r\gets\mathrm{Rec}(\mathrm{st},\stmt,\wit), and returns (π,r)(\pi,r).

Note that in the simulated experiment the witness reaches only Rec\mathrm{Rec}, after Sim\mathrm{Sim} has already fixed the proof, which is what happens in our sketched security proof upon a corruption. For Fiat–Shamir proofs, the adversary re-runs the prover against the same random oracle, so the randomness has to reproduce the proof under the answers the simulator programmed. We also allow the reconstruction algorithm to use the simulator’s retained state, which includes every random-oracle answer the simulator chose, and ask it to reconstruct randomness only for proofs that this simulator produced.

State reconstruction for Schnorr proofs#

The goal of this post is to show that Bulletproofs have honest prover state reconstruction. As a warm-up, we first recall honest prover state reconstruction for Schnorr proofs [Sch91], as observed by Bacho et al. [BLSW24]. We return to our example of proving knowledge of the secret key w\wit for a public key x=gw\stmt=g^\wit. Recall that to compute such a Schnorr proof, the prover samples a random element r←$Zpr\sample\ZZ_p, sends A=grA=g^r, and answers the challenge c=H(x,A)c=H(\stmt,A) with z=r+cwz=r+c\wit. To simulate such a proof, the simulator samples (c,z)←$Zp2(c,z) \sample \ZZ_p^2 uniformly and sets A=gzx−cA=g^z\stmt^{-c}, which satisfies the verification equation gz=Axcg^z=A\stmt^c. It then programs the random oracle to return cc on input (x,A)(\stmt,A), provided that this input has not already been queried.

Theorem 1 (Schnorr proofs have honest prover state reconstruction).

Schnorr’s proof of knowledge of a discrete logarithm has honest prover state reconstruction in the sense of Definition 1.

Proof (Reconstructing the Schnorr prover's randomness).

We construct an algorithm Rec\mathrm{Rec} that, given the simulator’s state (c,z)(c,z) and the witness w\wit, computes

r=z−cw(modp). r=z-c\wit\pmod p.

Since x=gw\stmt=g^\wit, this gives gr=gzx−c=Ag^r=g^z\stmt^{-c}=A, and the honest prover’s response is r+cw=zr+c\wit=z, so re-running the prover on rr against the programmed oracle reproduces π\pi. In the real experiment rr is uniform and the challenge c=H(x,A)c=H(\stmt,A) is uniform and independent of it. In the simulated experiment cc and zz are uniform and independent, and for fixed cc and w\wit subtracting cwc\wit is a bijection, so cc and rr are uniform and independent as well. Both experiments therefore give the same joint distribution of π\pi and rr, unless Sim\mathrm{Sim} has to program a point (x,A)(\stmt,A) that is already defined. Let the adversary make qHq_H random-oracle queries and qPq_P proof queries. Since AA is uniform in a group of order pp, a single proof hits an already defined point with probability at most (qH+qP)/p(q_H+q_P)/p, and a union bound over the qPq_P proofs shows that the two experiments differ by at most qP(qH+qP)/pq_P(q_H+q_P)/p, which is negligible in the security parameter.

The same argument applies to other Schnorr-type proofs whose responses are linear in the witness, such as proofs of equality of discrete logarithms or of knowledge of a Pedersen opening, where the reconstruction algorithm subtracts the challenge times the witness from each response.

For Schnorr proofs, honest prover state reconstruction is possible, so they can be simulated in adaptive protocols without assuming secure erasures upon corruption. However, a Schnorr proof sends one response per secret value, so the proof size grows linearly with the witness. To make protocols that use NIZKs for large witnesses more efficient, one can use Bulletproofs [BBB+18] instead of Schnorr proofs. In contrast to Schnorr proofs, Bulletproofs grow only logarithmically in the witness size, but like Schnorr proofs, they need no trusted setup and rely only on the discrete-logarithm assumption.

Pedersen commitments#

Before we turn to Bulletproofs, we need one more building block, namely Pedersen commitments [Ped91]. Intuitively, a commitment scheme works like a sealed envelope. In the commit phase, a committer puts a value mm into the envelope and hands it to a receiver, who cannot look inside. In the opening phase, the committer reveals mm, and the receiver checks it against the value sealed in the envelope. Proof systems use commitments to let the prover fix a value before it sees the verifier’s challenge, without revealing it yet.

To compute a Pedersen commitment to a single value, we need a second generator hh of our group. To commit to m∈Zpm\in\ZZ_p, the committer samples a random ρ←$Zp\rho\sample\ZZ_p and sends C=gmhρC=g^mh^\rho. To open the commitment, it reveals mm and ρ\rho, and the receiver checks that C=gmhρC=g^mh^\rho.

A commitment should satisfy two properties. First, it should be hiding, which means that CC reveals no information about mm. For Pedersen commitments, this holds since for every pair of values m≠m′m \neq m' and every ρ\rho, there is exactly one ρ′\rho' with gmhρ=gm′hρ′g^mh^\rho=g^{m'}h^{\rho'}. Every commitment could thus equally well contain any other value, and since ρ\rho is uniformly random, CC has the same distribution no matter which value it contains. Second, it should be binding, which means that the committer cannot open CC to a different value later. If it could, it would know two openings (m,ρ)(m,\rho) and (m′,ρ′)(m',\rho') with m≠m′m\neq m' and gmhρ=gm′hρ′g^mh^\rho=g^{m'}h^{\rho'}, from which it could compute the discrete logarithm log⁡hg=(ρ′−ρ)/(m−m′)\log_h g=(\rho'-\rho)/(m-m'). Pedersen commitments are thus binding as long as the discrete-logarithm assumption between gg and hh holds.

On the other hand, Pedersen commitments are equivocable. This means that anyone who knows ν=log⁡hg\nu=\log_h g can compute the randomness ρ′=ρ+ν(m−m′)\rho'=\rho+\nu(m-m') from the hiding property, and thus open a commitment C=gmhρC=g^mh^\rho to any other value m′m'. We call ν\nu the trapdoor of the commitment. Just like the simulator’s trapdoor for proofs, this trapdoor will become important later. Pedersen commitments also let us commit to whole vectors m∈Zpn\mathbf m\in\ZZ_p^n at once in a single group element. This works by using one generator per entry via C=hρ∏igimiC=h^\rho\prod_ig_i^{m_i}. Such a vector commitment is binding as long as nobody knows a discrete-logarithm relation between the generators, and anyone who knows the discrete logarithms of all generators to the base hh can again open it to any vector. To make sure that a security reduction can know such a relation while real protocol participants do not, one typically derives all generators through the random oracle by hashing public labels [BBB+18].

Bulletproofs#

At their core, Bulletproofs use an inner-product argument, which proves that two committed vectors have a given inner product. More formally, for generators g=(g1,…,gn)\mathbf g=(g_1,\ldots,g_n), h=(h1,…,hn)\mathbf h=(h_1,\ldots,h_n) and uu, and writing ga=∏igiai\mathbf g^{\mathbf a}=\prod_i g_i^{a_i}, the prover of an inner-product argument shows that it knows vectors a,b∈Zpn\mathbf a,\mathbf b\in\ZZ_p^n with P=gahbu⟨a,b⟩P=\mathbf g^{\mathbf a}\mathbf h^{\mathbf b}u^{\langle\mathbf a,\mathbf b\rangle} for a public group element PP. Inner-product arguments are useful for NIZKs because many statements can be expressed as inner products. Evaluating a committed polynomial with coefficient vector a\mathbf a at a point zz, for instance, is the inner product ⟨a,(1,z,…,zn−1)⟩\langle\mathbf a,(1,z,\ldots,z^{n-1})\rangle, and the constraints of an arithmetic circuit can be checked all at once through a random linear combination that collapses them into a single inner product [BCC+16].

To prove an inner product without sending 2n2n field elements, Bulletproofs use the split-and-fold technique of [BCC+16, BBB+18]. It splits both vectors into halves a1,a2\mathbf a_1,\mathbf a_2 and b1,b2\mathbf b_1,\mathbf b_2 and, for a challenge xx, folds them into a′=xa1+x−1a2\mathbf a'=x\mathbf a_1+x^{-1}\mathbf a_2 and b′=x−1b1+xb2\mathbf b'=x^{-1}\mathbf b_1+x\mathbf b_2 of half the length, whose inner product is ⟨a′,b′⟩=⟨a,b⟩+x2⟨a1,b2⟩+x−2⟨a2,b1⟩\langle\mathbf a',\mathbf b'\rangle=\langle\mathbf a,\mathbf b\rangle+x^2\langle\mathbf a_1,\mathbf b_2\rangle+x^{-2}\langle\mathbf a_2,\mathbf b_1\rangle. The folded vectors thus satisfy the same relation once the two cross terms ⟨a1,b2⟩\langle\mathbf a_1,\mathbf b_2\rangle and ⟨a2,b1⟩\langle\mathbf a_2,\mathbf b_1\rangle are accounted for. To account for these cross terms, the prover computes the two group elements L=g2a1h1b2u⟨a1,b2⟩L=\mathbf g_2^{\mathbf a_1}\mathbf h_1^{\mathbf b_2}u^{\langle\mathbf a_1,\mathbf b_2\rangle} and R=g1a2h2b1u⟨a2,b1⟩R=\mathbf g_1^{\mathbf a_2}\mathbf h_2^{\mathbf b_1}u^{\langle\mathbf a_2,\mathbf b_1\rangle}.

Given the two group elements LL and RR, both parties can fold the generators to half the length and set P′=Lx2PRx−2P'=L^{x^2}PR^{x^{-2}}, whose extra factors add exactly the two cross terms. Because Fiat–Shamir derives xx only after LL and RR are fixed, the prover cannot choose them to hide a wrong inner product. The result P′P' is again an instance of the same relation, but of half the length. To proceed, the prover applies this step recursively, until after log⁡2n\log_2 n rounds each vector is a single scalar. With this technique, the proof consists of 2log⁡2n2\log_2 n group elements and two scalars. Apart from a\mathbf a and b\mathbf b this prover uses no randomness.

One round of split and fold. The challenge x combines the two halves of each vector into a vector of half the length, while the cross terms L and R pair each half of one vector with the opposite half of the other and turn P into the statement for the halved vectors. After log2 n rounds two scalars remain.
Figure 4: Split and fold in the inner-product argument. Each round adds two group elements to the proof. The verifier folds the generators and the statement itself.

The protocol we have described so far is not zero-knowledge, since PP and the final scalars are functions of the vectors. To make it zero-knowledge, Bulletproofs+ [CHJ+22] use the hiding property of Pedersen commitments and add a random power of hh to every group element. (Bulletproofs+ actually prove weighted inner products, but for this post we set all weights to 11.) First, the statement itself becomes a hiding commitment P=gahbu⟨a,b⟩hαP=\mathbf g^{\mathbf a}\mathbf h^{\mathbf b}u^{\langle\mathbf a,\mathbf b\rangle}h^{\alpha} with a random blinding exponent α\alpha. Then, in every round, the prover samples two fresh exponents dL,dR←$Zpd_L,d_R\sample\ZZ_p and sends the blinded cross terms

L=g2a1h1b2u⟨a1,b2⟩hdL,R=g1a2h2b1u⟨a2,b1⟩hdR. L=\mathbf g_2^{\mathbf a_1}\mathbf h_1^{\mathbf b_2}u^{\langle\mathbf a_1,\mathbf b_2\rangle}h^{d_L},\qquad R=\mathbf g_1^{\mathbf a_2}\mathbf h_2^{\mathbf b_1}u^{\langle\mathbf a_2,\mathbf b_1\rangle}h^{d_R}.

Since hdLh^{d_L} and hdRh^{d_R} are uniformly random, LL and RR now reveal no information about the witness vectors. Folding works as before, but the prover additionally keeps track of the blinding exponent of the folded commitment P′=Lx2PRx−2P'=L^{x^2}PR^{x^{-2}}, which is α′=α+x2dL+x−2dR\alpha'=\alpha+x^2d_L+x^{-2}d_R.

In the last round, where the prover would otherwise send the scalars aa and bb in the clear, it now proves that it knows aa, bb and α\alpha with a Schnorr-like step. It samples r,s,δ,η←$Zpr,s,\delta,\eta\sample\ZZ_p, sends the two commitments

A=g∗rh∗surb+sahδ,B=urshη A=g_\ast^{r}h_\ast^{s}u^{rb+sa}h^{\delta},\qquad B=u^{rs}h^{\eta}

for the folded generators g∗g_\ast and h∗h_\ast, and answers the challenge ee with

r′=r+ae,s′=s+be,δ′=η+δe+αe2. r'=r+ae,\qquad s'=s+be,\qquad \delta'=\eta+\delta e+\alpha e^2.

Just like in a Schnorr proof, the random rr and ss hide aa and bb in the responses. The verifier then checks that Pe2AeB=g∗r′eh∗s′eur′s′hδ′P^{e^2}A^eB=g_\ast^{r'e}h_\ast^{s'e}u^{r's'}h^{\delta'}, where AA and BB provide the cross terms rb+sarb+sa and rsrs that show up in ur′s′u^{r's'}.

Put together, the Bulletproof inner-product argument for vectors of length nn consists of the blinded cross terms of all k=log⁡2nk=\log_2 n rounds and the messages of the final Schnorr-like step,

π=(L1,R1,…,Lk,Rk,A,B,r′,s′,δ′). \pi=(L_1,R_1,\ldots,L_k,R_k,A,B,r',s',\delta').

With Fiat–Shamir, the challenge xjx_j of round jj and the last challenge ee are hashes of PP and all previous messages, so the verifier can recompute them from π\pi and fold the generators and PP itself.

With these changes, we get a zero-knowledge argument that the prover knows a\mathbf a, b\mathbf b and α\alpha with P=gahbu⟨a,b⟩hαP=\mathbf g^{\mathbf a}\mathbf h^{\mathbf b}u^{\langle\mathbf a,\mathbf b\rangle}h^{\alpha}. Compared to the plain argument, it costs only two more group elements and one more scalar. The original Bulletproofs [BBB+18] make the argument zero-knowledge in a different way, by running it on randomly masked vectors. In this post we focus on the approach of Bulletproofs+, and come back to the original one in Corollary 1.

Simulating Bulletproofs#

To simulate Bulletproofs without knowing a valid witness, the simulator can follow a strategy similar to the one for Schnorr proofs. In the rounds of the argument, the simulator samples every LL and RR as a uniformly random group element. These are distributed exactly like the blinded cross terms of an honest prover. The round challenges come from the random oracle as usual. For the last step, the simulator samples the responses r′,s′,δ′r',s',\delta' and the challenge ee uniformly, together with a random group element BB, and then computes AA such that the verification equation holds. Finally, as in the Schnorr simulation, the simulator programs the random oracle to return ee on the transcript ending with AA and BB, provided that this input has not already been queried.

Let us now try to reconstruct the prover’s randomness for such a simulated proof, once we know the witness a\mathbf a, b\mathbf b and α\alpha. We do this in the opposite order in which the simulator computed the proof. For the last step, we can apply the Schnorr trick from above, computing r=r′−aer=r'-ae and s=s′−bes=s'-be. For the blinding exponents, however, we run into a problem. Since the simulator sampled LL as a random group element, we need a dLd_L for which the honest prover would have sent the same LL, namely

hdL=L g2−a1h1−b2u−⟨a1,b2⟩. h^{d_L}=L\,\mathbf g_2^{-\mathbf a_1}\mathbf h_1^{-\mathbf b_2}u^{-\langle\mathbf a_1,\mathbf b_2\rangle}.

The same holds for dRd_R in every round and for η\eta in BB. Finding such a dLd_L means computing a discrete logarithm. However, it can also be found with the trapdoor of the Pedersen commitments involved, which consists of the discrete logarithms of all generators to the base hh.

Our simulator can obtain this trapdoor because the generators come from the random oracle. In the simulated experiment of Definition 1, the simulator answers all random-oracle queries itself, so it also decides what the generators are. It answers the query for hh honestly, but every other generator query with hθh^{\theta} for a fresh uniform exponent θ\theta. Since hθh^\theta is again a uniformly random group element, the adversary cannot notice this change. This way, the simulator knows scalars κ,λ,ν,ω\boldsymbol\kappa,\boldsymbol\lambda,\nu,\omega with

gi=hκi,hi=hλi,g=hν,u=hω, g_i=h^{\kappa_i},\qquad h_i=h^{\lambda_i},\qquad g=h^{\nu},\qquad u=h^{\omega},

which we call the generator trapdoor. Once the simulator knows the generator trapdoor, it also knows the discrete logarithms of all folded generators, since folding only raises the generators to known powers of the challenges and multiplies them. For example, log⁡hgi′=x−1κi+x κi+n/2\log_h g'_i=x^{-1}\kappa_i+x\,\kappa_{i+n/2}. In the same way, it samples every LL, RR and BB as a power of hh and remembers the exponents, which we denote by θL\theta_L, θR\theta_R and θB\theta_B. Note that this needs no additional assumption, since the simulator already programs the random oracle for the Fiat–Shamir challenges.

With the generator trapdoor, every commitment of the form C=hρgmhm′um′′C=h^{\rho}\mathbf g^{\mathbf m}\mathbf h^{\mathbf m'}u^{m''} is simply a power of hh,

C=h ρ+⟨κ,m⟩+⟨λ,m′⟩+ω m′′. C=h^{\,\rho+\langle\boldsymbol\kappa,\mathbf m\rangle+\langle\boldsymbol\lambda,\mathbf m'\rangle+\omega\,m''}.

So if the simulator sampled CC itself as a power of hh, it can open CC to any messages by setting

ρ=log⁡hC−⟨κ,m⟩−⟨λ,m′⟩−ω m′′. \rho=\log_h C-\langle\boldsymbol\kappa,\mathbf m\rangle-\langle\boldsymbol\lambda,\mathbf m'\rangle-\omega\,m''.

Opening a commitment thus costs only one subtraction, just like reconstructing the Schnorr prover’s randomness in Theorem 1.

State reconstruction for Bulletproofs#

With the generator trapdoor and the exponents θL\theta_L, θR\theta_R and θB\theta_B, the reconstruction algorithm can now compute all blinding exponents, which gives our main result.

Theorem 2 (Bulletproofs have honest prover state reconstruction).

The zero-knowledge inner-product argument of Bulletproofs+ [CHJ+22] with weight 11, made non-interactive with Fiat–Shamir and with generators derived from the random oracle, has honest prover state reconstruction in the sense of Definition 1.

For every random value of the honest prover, the simulated proof contains a value that this randomness has to reproduce. The table lists these pairs, in the order in which the reconstruction algorithm computes them.

Randomness of the honest proverSimulated value it has to reproduceHow the reconstruction algorithm computes it
dLd_L and dRd_R in every roundL=hθLL=h^{\theta_L} and R=hθRR=h^{\theta_R}open LL and RR with the generator trapdoor
rr and ssr′r' and s′s'solve r′=r+aer'=r+ae and s′=s+bes'=s+be
η\etaB=hθBB=h^{\theta_B}open BB with the generator trapdoor
δ\deltaδ′\delta'solve δ′=η+δe+αe2\delta'=\eta+\delta e+\alpha e^2

The last message AA needs no row of its own, because once all other values agree, the verification equation leaves only one choice for AA. The honest prover uses 2k+42k+4 random values for the k=log⁡2nk=\log_2 n rounds, and the simulator samples exactly as many values, one for each random value of the honest prover.

Proof (Reconstructing the prover's randomness).

We construct an algorithm Rec\mathrm{Rec} that, given the simulator’s state and a valid witness (a,b,α)(\mathbf a,\mathbf b,\alpha), computes the randomness of the honest prover in the order of the table. In each round, it knows the discrete logarithms of the current generators and sets

dL=θL−⟨κ2,a1⟩−⟨λ1,b2⟩−ω⟨a1,b2⟩ d_L=\theta_L-\langle\boldsymbol\kappa_2,\mathbf a_1\rangle-\langle\boldsymbol\lambda_1,\mathbf b_2\rangle-\omega\langle\mathbf a_1,\mathbf b_2\rangle

and dRd_R in the same way, where κ2\boldsymbol\kappa_2 and λ1\boldsymbol\lambda_1 contain the discrete logarithms of g2\mathbf g_2 and h1\mathbf h_1. With these exponents, the honest prover sends the same LL and RR as the simulator, and Rec\mathrm{Rec} folds a\mathbf a, b\mathbf b and α\alpha as the honest prover does. For the folded aa, bb and α\alpha, it then sets

r=r′−ae,s=s′−be,η=θB−ωrs,δ=e−1(δ′−η−αe2), r=r'-ae,\qquad s=s'-be,\qquad \eta=\theta_B-\omega rs,\qquad \delta=e^{-1}(\delta'-\eta-\alpha e^2),

so that the honest prover sends the same BB and the same responses. It remains to show that the honest prover also computes the same AA, which the simulator derived from the verification equation. The valid witness opens PP, and folding preserves this relation, so the folded witness opens the folded PP. The honest AA therefore satisfies the verification equation, in which every other value agrees with the simulation, so it equals the simulated AA. Re-running the prover on the reconstructed randomness thus reproduces the whole proof.

The generators are uniformly random in both experiments. In the real experiment, the random values dL,dR,r,s,δ,ηd_L,d_R,r,s,\delta,\eta are uniform, and the last challenge is uniform and independent of them. In the simulated experiment, the exponents θL,θR,θB\theta_L,\theta_R,\theta_B and the responses r′,s′,δ′r',s',\delta' are uniform and independent of the last challenge. For fixed challenges and witness, each step of Rec\mathrm{Rec} subtracts a value that the witness and the previous steps determine, and possibly multiplies by e−1e^{-1}, so the reconstruction is a bijection and the reconstructed random values are uniform as well. Both experiments therefore give the same joint distribution of proof and randomness, unless Sim\mathrm{Sim} has to program a point that is already defined. As for Schnorr proofs, the simulator programs only one point per proof, and this point contains the uniformly random BB. A union bound over the qPq_P proofs bounds this probability by qP(qH+qP)/pq_P(q_H+q_P)/p, as in the proof of Theorem 1.

Remark 1 (Other weights).

Bulletproofs+ prove the weighted inner product ∑iaibiyi\sum_i a_ib_iy^i for a weight y∈Zp∗y\in\ZZ_p^*. Theorem 2 carries over to every such weight. The weight only adds known powers of yy to some exponents of the cross terms, the folding and the final step. The reconstruction algorithm therefore subtracts slightly different known values, and every step remains a bijection.

Using the trapdoor in a larger protocol#

If a security proof relies on the generator trapdoor, it can no longer rely on the binding property of the Pedersen commitments, since the reduction can equivocate the commitments. If the surrounding protocol needs binding, its security proof therefore has to establish binding before it switches to the generator trapdoor. This adds two experiments to the sequence from the introduction, which sit between the honest execution and the experiment with simulated NIZK proofs.

In the first experiment, the random oracle still answers the generator queries honestly, but the experiment aborts as soon as the adversary opens a commitment to two different values. This experiment differs from the honest execution only if the abort happens. An adversary that causes an abort reveals a discrete-logarithm relation between the honest generators, an event with negligible probability under DLog.

In the second experiment, the random oracle answers the generator queries with powers of hh whose exponents the reduction knows. This switch does not change the distribution of the generators, so the adversary cannot distinguish the second experiment from the first. From now on, the reduction no longer needs binding, since every binding break already leads to an abort. Extraction from adversarial Bulletproofs [GOP+22, DG23] is unaffected, because the adversary never learns the trapdoor.

From inner products to range proofs and arithmetic circuits#

So far, we have reconstructed the randomness of the inner-product argument itself. For a full range proof or arithmetic-circuit proof, we also need to reconstruct the randomness used in the commitments and masking steps around that argument. We start with the range proof of Bulletproofs+, then turn to the masked-vector construction used in the original Bulletproofs.

Corollary 1 (Range proofs and arithmetic circuits).

The range proofs of Bulletproofs+ [CHJ+22] and of Bulletproofs [BBB+18], as well as the arithmetic-circuit protocol of Bulletproofs, made non-interactive with Fiat–Shamir and with generators derived from the random oracle, have honest prover state reconstruction in the sense of Definition 1.

Proof.

The range proof of Bulletproofs+ shows that a Pedersen commitment V=gvhγV=g^vh^\gamma contains a value v∈[0,2n)v\in[0,2^n). Its prover first commits to the bits aL\mathbf a_L of vv and to aR=aL−1n\mathbf a_R=\mathbf a_L-\mathbf 1^n as A=gaLhaRhαA=\mathbf g^{\mathbf a_L}\mathbf h^{\mathbf a_R}h^{\alpha}. After two challenges yy and zz, it runs the argument of Theorem 2 with weight yy on a statement that both parties compute from AA and VV. Our simulator samples AA as a power of hh, so Rec\mathrm{Rec} obtains α\alpha with the generator trapdoor and then proceeds as in the proof of Theorem 2, which covers weight yy by Remark 1. The simulator now programs a second point per proof for (y,z)(y,z), which changes the bound to 2qP(qH+2qP)/p2q_P(q_H+2q_P)/p.

The range proof and the circuit protocol of Bulletproofs run the plain inner-product argument on masked vectors. We can reconstruct their randomness in a similar way. In the range proof, the prover commits to vectors a,b∈Zpn\mathbf a,\mathbf b\in\ZZ_p^n derived from the witness in a commitment AA, and to random masks sL,sR←$Zpn\mathbf s_L,\mathbf s_R\sample\ZZ_p^n in a commitment SS. The inner product of the masked vectors a+XsL\mathbf a+X\mathbf s_L and b+XsR\mathbf b+X\mathbf s_R is a polynomial t(X)=t0+t1X+t2X2t(X)=t_0+t_1X+t_2X^2, where t0t_0 depends only on the witness (up to public terms that encode the range). The prover commits to t1t_1 and t2t_2 in T1T_1 and T2T_2, and after a challenge xx, it runs the plain argument on ℓ=a+xsL\boldsymbol\ell=\mathbf a+x\mathbf s_L and r=b+xsR\boldsymbol r=\mathbf b+x\mathbf s_R, whose inner product is t(x)t(x). Every commitment hides its content behind a random blinding exponent, so the prover also sends two blinding responses. The response μ\mu combines the blinding exponents of AA and SS in the same way as ℓ\boldsymbol\ell and r\boldsymbol r combine the vectors and the masks, and τx\tau_x combines those of VV, T1T_1 and T2T_2 in the same way as t(x)t(x) combines the coefficients. With these responses, the verifier checks in two verification equations that ℓ\boldsymbol\ell and r\boldsymbol r match AA and SS, and that t(x)t(x) matches VV, T1T_1 and T2T_2. The circuit protocol works in the same way, with more commitments and a polynomial of degree six. Our simulator samples ℓ\boldsymbol\ell, r\boldsymbol r, μ\mu and τx\tau_x uniformly and every commitment as a power of hh, except for SS and T1T_1, which it derives from the two verification equations. With the witness, Rec\mathrm{Rec} opens the sampled commitments with the generator trapdoor and solves the responses for the masks and the remaining blinding exponents, as for Schnorr proofs. The honest SS and T1T_1 then satisfy the verification equations and hence equal the simulated ones, where the equality for T1T_1 again relies on the witness being valid. The plain inner-product prover is deterministic. Running it on the same masked vectors reproduces its transcript without any further reconstruction. All equations that Rec\mathrm{Rec} solves are linear with nonzero coefficients, so the reconstructed random values are uniform, and the bound is again 2qP(qH+2qP)/p2q_P(q_H+2q_P)/p.

Conclusion#

Simulating NIZK proofs is a standard step in security proofs, but it becomes much harder once the adversary can corrupt parties adaptively. Upon a corruption, the reduction has to reveal the randomness behind every proof of the corrupted party, including the proofs that it simulated without a witness. Since guessing the corrupted parties in advance is too costly in general, the reduction either relies on secure erasures or has to reconstruct randomness that is consistent with the simulated proofs. For Schnorr proofs, this reconstruction was already established and takes a single subtraction, but for Bulletproofs it was left as an open problem [BLSW24].

In this post, we showed that Bulletproofs have honest prover state reconstruction, both the zero-knowledge argument of Bulletproofs+ [CHJ+22] and the range proof and circuit protocol of the original Bulletproofs [BBB+18]. The key observation is that every group element that the prover blinds is a Pedersen commitment, and the remaining group elements follow from the verification equations or from a deterministic computation on values that are already fixed. A simulator that programs the generators through the random oracle knows their discrete logarithms and can therefore open each blinded commitment to the witness it learns upon corruption. Protocols that simulate Bulletproofs in their security proof therefore no longer need to assume secure erasures for the randomness of these proofs.

References

[GMR85]
Shafi Goldwasser, Silvio Micali, Charles Rackoff
STOC 1985
[BFM88]
Manuel Blum, Paul Feldman, Silvio Micali
STOC 1988
[BR93]
Mihir Bellare, Phillip Rogaway
ACM CCS 1993
[BBB+18]
Benedikt Bünz, Jonathan Bootle, Dan Boneh, Andrew Poelstra, Pieter Wuille, Greg Maxwell
IEEE Symposium on Security and Privacy 2018
[GOS06]
Jens Groth, Rafail Ostrovsky, Amit Sahai
EUROCRYPT 2006
[CHJ+22]
Heewon Chung, Kyoohyung Han, Chanyang Ju, Myungsun Kim, Jae Hong Seo
IEEE Access 2022
[BLSW24]
Renas Bacho, Julian Loss, Gilad Stern, Benedikt Wagner
ASIACRYPT 2024
[Ped91]
Non-Interactive and Information-Theoretic Secure Verifiable Secret Sharing
Torben Pryds Pedersen
CRYPTO 1991
[Sch91]
Claus-Peter Schnorr
Journal of Cryptology 1991
[FS86]
How to Prove Yourself: Practical Solutions to Identification and Signature Problems
Amos Fiat, Adi Shamir
CRYPTO 1986
[BCC+16]
Jonathan Bootle, Andrea Cerulli, Pyrros Chaidos, Jens Groth, Christophe Petit
EUROCRYPT 2016
[CGJKR99]
Adaptive Security for Threshold Cryptosystems
Ran Canetti, Rosario Gennaro, Stanisław Jarecki, Hugo Krawczyk, Tal Rabin
CRYPTO 1999
[DG23]
Quang Dao, Paul Grubbs
EUROCRYPT 2023
[GOP+22]
Chaya Ganesh, Claudio Orlandi, Mahak Pancholi, Akira Takahashi, Daniel Tschudi
EUROCRYPT 2022