Processing math: 100%

Web2 Nullifiers using vOPRF

Recommended to read Overview for understanding.

Abstract

Idea of having nullifiers for Web2 IDs is very promising. There's an explanation of the idea and how we can achieve this. As it was said - we can build a lot of applications on top of that, and this would require users to go through the same process for all apps. But can we do better? YES!

What we can do - is to create a one big global system that will hold registered identities, and people will be able to reuse them across different apps (of course while preserving nullifiers & anonimity) - kinda like global Semaphore for Web2 Identities.

In the next section I'll explain how we can do that.

How it works

In the end of the overview explanation I said:

instead of revealing nullifier we can reveal hash(nullifier,AppID) - where AppID is a unique identifier of the app, and that's gonna be our real nullifier.

That's actually the key for building such system.

We can deploy one registry smart-contract. We'll set AppID=0. We also gonna keep pseudonym=hash(sG, AppID), where, s is "private key" of OPRF MPC, and G=hashToCurve(UserID). All the identities will be stored in Merkle Tree, and pubkey=hash(pseudonym,commitment1) will be stored in its leaves. For those who forgot, commitment1=hash(UserID, salt).

Now, let's say we registered our github identity in our global system and there's an app that gives airdrop to their contributors (of course we want to claim airdrop anonymously). The airdrop app will need to set their own AppID, different from 0, because 0 is already taken; this can get checked by registry smart-contract. It will also need to create a merkle tree of github usernames that are eligible for airdrop (our github username is in that list).

Now, to claim airdrop anonymously we'll need to create zk proof with the following parameters:

  • Public: AppID, nullifier, registryRoot, appRoot
  • Private: UserID, sG, salt

and the constraints: pseudonymhash(sG, 0) commitment1hash(UserID, salt) pubkeyhash(pseudonym,commitment1) registryRoot=merkleTreeVerify(pubkey) appRoot=merkleTreeVerify(UserID) nullifier=hash(sG, AppID)


As you can see, the pair (sG, salt) will serve as an action key, and $sG$ itself will be a viewing key.

Additional comments

While reading this, you might ask the same questions we have right now. For example, how we can prevent users spamming OPRF? It also makes sense to make system even more general and give people an option to specify which OPRF provider they gonna use and of what id-provider they will use (there can be many even for the same id-provider, e.g. bridged by zkEmail, zkTLS & OpenPassport). While they are important, they are not in the scope of this blog post.