Skip to content
Proof of concept for identity derived from zerowallet
JavaScript HTML
Branch: master
Clone or download

Latest commit

Fetching latest commit…
Cannot retrieve the latest commit at this time.


Type Name Latest commit message Commit time
Failed to load latest commit information.

Identity Proof of Concept

Here's a brief rundown of the architecture and setup behind our identity proof of concept that we just launched

Check out the app here:


The goal of this application to to equip a user with a private key while providing UX similar to a traditional web app.

In the demo application, a user stores a private note in IPFS that is encrypted/decrypted with their private key.

For the first iteration of our identity system, we decided to use the ZeroWallet scheme.

To Run Locally

  • Install dependencies
    • yarn
  • Setup environment
    • cp .env.example .env
    • change variables
  • Run
    • yarn dev
    • Given defaults, your server will be on port 8080 and a React app will be on port 3000


ZeroWallet is a simple scheme where a user generates a private key with two passwords, and unlocks their account on login using just a username and password.

It makes use of Shamir's Secret Sharing and zero-knowledge proofs to allow recovery of private keys.

ZeroWallet uses 2 passwords and 3 shards. Any 2 shards can reconstruct the privateKey.

As a brief overview of the components:

  • account password: this is the password that a user generally uses to login to their account. Imagine a normal username/password signin
  • recovery password: this is the password that a user uses to recover their private key
  • shard1: this shard is just a hash of the account password (shard1 = sha256(accountPassword))
  • shard2: this is the fancy part of the scheme that makes use of zero-knowledge proofs. You can read more about the nitty gritty details Here. For the purpose here, think of it as shard2 = serverTransform(sha256(recoveryPassword)). What actually happens is similar to Diffie-Hellman key exchange. The important part of this is that the server gets no information about the user's share (sha256(recoveryPassword)), and a malicious hacker gets no information about the server's share by sending spoof requests.
  • shard3: this shard is derived from the first two using Shamir's Secret Sharing (SSS). SSS can come up with an essentially endless number of shards for a given private key. So shard3 is actually unique per-device

Signup flow (I'm totally new here)

  • user enters, username, accountPassword, and recoveryPassword
  • recoveryPassword is hashed and "randomized" into a value we'll call alpha, then sent to the server
  • the server generates an arbitrary 256-bit key zk_key.
  • zk_key is combined with alpha to produce beta which is sent back to the user
  • beta is "derandomized" to produce shard2
  • user generates shard1 = sha256(accountPassword)
  • user acquires privateKey = SSS.combine(shard1, shard2)
  • user generates shard3 = SSS.newShard(privateKey)
  • user stores shard3 in localStorage and logs into the safe

Login flow (I've already logged onto this device before)

  • user enters username and accountPassword
  • user generates shard1 = sha256(accountPassword)
  • user gets shard3 from localStorage
  • user acquires privateKey = SSS.combine(shard1, shard3) and loogs into the safe

Recovery flow (I want to login on a new device)

  • This is the exact same flow as Signup except that the server uses the zk_key that's already in the database


If a user is only encrypting data for themselves, they can just encrypt/decrypt it with their privateKey. However, we wanted to build this in such a way that a "sharing" or "social" component could easily be added.

To do this, you don't want to have to re-encrypt your note for every friend that you want to share it with. This leads to a lot of data replication, and also puts a big load on the user since everytime they edit the note, it must be re-encrypted for every one of their friends.

Instead, we encrypt the note with a symmetric key (cipherKey). Add the cipher text to IPFS. And then encrypt the cipherKey with the user's privateKey. Now, we have a simple setup that can be easily expanded to shared notes. If I want to share a note with you, I don't have to re-encrypt the entire note for your publicKey. I just have to encrypt and send you the cipherKey


I separated all "key" functionality into it's own module: keystore. Any code that touches private keys is always susceptible to attack (malicious packages, etc). We've discussed writing this module using WASM which essentially gives us a secure "black box" that other parts of our code can't acess, keeping these keys safe.


For the time being, we store just 4 things in the database: username, zk_key, safe_cid, and safe_key.

The first two are for ZeroWallet. username is self-explanatory, zk_key is the server's "share" of the second shard of the user's privateKey. By itself, it reveals absolutely nothing about the second shard, but it can be used to transform the user's recovery password in such a manner that they can recover the second shard without revealing any information to the server.

safe_cid is the current CID of the encrypted safe, and safe_key is an encrypted cipherKey for the safe. safe_cid and safe_key are currently stored in our database for convenience sake, but would eventually be moved to some form of decentralized storage (OrbitDB spike coming as we speak!)

You can’t perform that action at this time.