Collaborative SNARKs

(See https://eprint.iacr.org/2021/1530.)

Suppose we have parties, represented by integers , that each hold some private witness . We would like to verifiably compute some function (encoded as a circuit ) on all of their inputs while keeping secret. In other words, we want to generate a SNARK proof that while preserving privacy. For example, suppose we want to prove that all parties have some trust scores that fall within a certain range of each other, but we don’t want to reveal anyone’s exact trust score.

One way we could do this is by using MPC Fully Homomorphic Encryption (FHE). Each party would feed encryptions to a server, which then runs a circuit representing SNARK proof generation within FHE. The server would then give the encrypted proof for users to collectively decrypt. However, there are two drawbacks to this approach, namely that (i) it requires a central server to run the circuit computation, and (ii) FHE is expensive. What if instead we wanted to verifiably compute in a decentralized way? This is what Collaborative SNARKs let us do.

At a high level, a Collaborative SNARK will be a circuit representing the SNARK proving protocol on some private inputs and public inputs . Each party has their private inputs to only some of the input wires of this circuit. We need to compute this circuit in a distributed way, where each party is computing their own circuit, but we haven’t yet figured out what parties should be plugging in for the wires that correspond to the other parties’ secrets.

It turns out the parties will distribute their secret to the other parties in a way that hides the secret, using something called Secret Sharing. We’ll discuss this in more detail before returning to the high-level Collaborative SNARK protocol.

Secret Sharing

In this post, we introduce the notion of secret sharing, discuss how to perform arbitrary circuit computations using a tool called Beaver triples, which will lead us into our next post on Collaborative SNARKs.

Suppose we have some secret and parties, represented by integers We want to somehow “split” this secret among the parties, so that no one can recover unless all parties communicate with each other. This is called “secret-sharing”.

The most basic scheme is to give each party a share such that . How can we ensure this doesn’t reveal any information about unless all parties collude? We can simply let be randomly generated (say from for some large prime ), and set . Then each individually is distributed uniformly at random (in ), so any parties cannot uncover information about by talking to each other. There are also other secret sharing schemes, such as Shamir Secret Sharing. It is also possible to define a -of- secret sharing scheme, which just means that if at least parties collude then they can recover the secret, and otherwise parties cannot gain any information about the secret. We do not go into detail in this blog post.

Now we want to see how to do a few basic circuit operations on these secret shares. Throughout, let denote the set of secret shares of

Basic circuit operations on secret shares

Suppose we have secret shares and Then we can do the following operations on our secret(s) and obtain the corresponding secret shares:

Question: How do we multiply two secrets?

For example, we can’t just multiply our secret shares to obtain secret shares of , since in general .

It turns out that we need (i) communication and (ii) some pre-generated random shares that have some nice correlation. You can think of the purpose of these random values as blinding factors that allow parties to reveal expressions involving their shares that other parties can then can compute on, without revealing the shares themselves. These random shares are called Beaver triples, which we describe below.

Beaver Triples

At the moment, the parties have shares and . Here is the additional information we need:

A Beaver triple is a triple of random shares where . Suppose a trusted administrator chooses secret values of , , and , then distributes shares of , , to the parties (without revealing , , ).

We show how this auxiliary share can be used to help the party shares for .

The key is to notice that because and are random values, their shares are also random. Therefore, it is safe for a party to reveal their share of since blinds . In fact, it is safe for all parties to reveal their shares , since reconstructing still hides .

Now we can compute as follows, using the existing basic circuit operations previously outlined.

Multiply two secrets:

To recap, given secret shares and , we can now obtain secret shares of the following:

Note that these four operations allow us to perform an arbitrary circuit computation (with addition and multiplication gates) on secrets, while staying in secret share form.

Fitting everything together

Recall that we wanted to somehow do a distributed computation of a circuit representing a SNARK prover on secret inputs , but we didn’t know what each party should plug into the wires corresponding to secrets they don’t own. Now that we have a secret sharing scheme, we can simply have each party compute secret shares of their secret and distribute them to the other parties.

Finally, we already showed earlier how to do arbitrary circuit computations on secrets in a distributed, secret share form. This means the parties can compute secret shares of the proof, and do a round of communication to sum up their shares and recover the desired SNARK proof!