Blindvault - Privacy-Preserving Credential Issuance for the Zcash Ecosystem

Hello, everyone, I’m writing to get feedbacks and suggestions on something I’ve been working on.

BlindVault

Blindvault is a privacy-preserving credential issuance service that allows clients(applications) to issue and verify one time anonymous credentials without tracking users. It makes uses of BLS12-381 blind signatures and DLEQ proofs alongside deterministic key derivation. Blindvault ultimately serves as a middleware (infrastructure) that can be used by any application through API calls to perverse users privacy, it can be used for private airdrop, access controls, community votes and similar apps.

Description

Many apps tries to preverse users privacy but still fail to do so, either because they need to track a users(accounts) to ensure their are ideed eligibile for rewards or other identifiers. All this weaken users privacy.

Blindvault is a middleware service that only focus on issue and verifying credentials, it let the application handles all the logic(e.g: eligibility checks…)

Blindvault stays stateless, application remains responsible for deciding who’s eligible to receive a credentials, while blindvault issues unlinkable blind signatures that can be redeemed later(technically redemption must only be done once, but it’ll be up to the application to either decide to allow double redemption or not). During redemption the service verifies the credentials without learning which credential is being redeemed

Blindvault was designed as a reusable infrastructure, instead of having every project to implement blind signature, replay attacks protection and key management independently.

How it works

The client needs to generate a scalar, which would be used for both blinding and unblinding, the scalar is as sensitive as the private key of a wallet. It can generated randomly or deterministically( hash(priv_key|appname) ), that’s up to the client to decide

  1. Client blind a message using the scalar (the content of the message is not important, it can be anything)
  2. Client send the blinded message to blindvault though API POST /v1/credential/issue
    {
    "blinded_message": "<hex-encoded compressed G1 point(blinded message)>/",
    "credential_class": "<credential class>"
    }
  3. The client on success receive the blind signature, the public key of the server, the epoch, and the DLEQ proof(although the server public key must be publicly available so anyone can verify that it was indeed signed by the server)
    {
    "blind_signature": "",
    "public_key": "",
    "key_epoch": "",
    "proof": {
    "r1": "",
    "r2": "",
    "s": "",
    "c": ""
    }
    }
  4. The client then make uses of the scalar they generated and unblind the blind signature
  5. The client can use the unblinded signature for redemption POST /v1/credential/consume
    {
    "unblinded_signature": "<hex-encoded compressed G1 point>",
    "witness": "<hex-encoded compressed G1 point>",
    "credential_class": "<credential class>",
    "key_epoch": "<epoch>"
    }
    The client would mainly receive two type of response valid (200) and failure(409)
    valid: for one time redemption
    failure: when double or more redemption of the same credentials occurs
    All others failures: invalid signature, witness, unsupported epoch fall into 400 error code

Technical Approach

Blindvault is build as a layered system, that allow a clear separation between cryptographic operations, logic, storage, and transport into independent components, that can be tested indivudally.

The core cryptography is BLS12-381 blind signatures. With each credentials belonging to a class, and signing keys derived deterministically from a master seed using HKDF-SHA256 and domain separation. They is no longer a need to store private keys independently. Every credentials that is issued comes with a DLEQ proof which is used to verify that the server used the correct signing key(ensures no tampering, because public key is available online).

Replay attacks are prevented by using deterministic nullifiers derived from credentials, class and epoch.

Use case

Blindvault can be used for many project such as anonymous airdrops, community voting, and many more.

BlindVault guarantees:

  • unlinkability between issuance and redemption,
  • cryptographic verification of issued credentials,
  • detection of replay attempts,
  • namespace isolation between credential classes,
  • verification that signatures originate from the expected derived key.

Trust Assumptions

  • Blindvault assumes that the application correctly verified that the user is eligible before requesting credentials to be issued
  • The master key is protected (if the master ends up being compromise, anyone can forge credentials)

What’s Built

  • Javascript client(blind, DLEQ proof verification and Unblind) (can be tested with the Go server)
  • Go server( Handles the issuance, consumption of credentials, with all security concern)

Features

  • Deterministic Key Derivation
  • Replay protection using deterministic nullifiers
  • In-memory backend for testing
  • Credential namespace isolation
  • DLEQ proof generation and verification
  • REST API for credential issuance and consumption

Working On:

  • Epoch and class revocation (completed) (revoking all epoch and class is the same as invalidating all credentials issued by blindvault)
  • Distributed Key Generation
  • Trusted‑dealer threshold
  • Threshold DLEQ proof generation
  • Batch Verification

I’m looking forward to hearing you’re feedbacks and suggestions

Github: GitHub - rawbytedev/blindvault · GitHub

Since blinding can be handled by the application itself, does Blindvault ever see the unblinded message during issuance, or does the unlinkability guarantee hold no matter which side does the blinding step?

1 Like

No blindvault never sees the unblinded message.
Blindvault only receives the blinded point which is obtain by computing a scalar with the message, because the scalar stays entirely on the client side, the server cannot reverse the blinding therefore can’t unblind, this ensure unlinkability between issuance and redemption

For the person performing the blinding:
It must be done by the client(application on user’s behalf), so blindvault doesn’t handle the blinding for the client

And an important thing to take not of it the scalar which can be randomly or deterministically generated, at first I opted for randomly generated scalar but this would introduce many UX challenge which brought me to propose both approach. The scalar is important like a wallet private key, with deterministic generation (many scalar can be generated, while also removing the need for the user to store all scalar)

The unlinkability is present as long as no one other than the client has access to the scalar, and the server can’t mathematically unblind a blinded signature, this is true whether the scalar is randomly or deterministically derived.

Because of blindvault we might know that someone obtained a signed message from the server, but we can’t know if it’s that same person who is making use of it (unlinkability)

I’ve mostly omitted all the mathematics operation in my answer if you’d like to know more about it I’ll make sure to share it
Also blindvault aims to for unlinkability between issuance request and redemption

I’ve just updated, blindvault, the new feature includes revocation of epochs and class. As it looks to me the rotation of the master seed doesn’t look that critical because revoking class and epochs is faster, but master seed rotation should be done securely and be rarely touch to avoid unexpected circustamces.

So as of now key rotations can be done directly by revoking, class or epochs.
Honestly it doesn’t look like a master seed rotation at all but it’s best to avoid exposing or creating such endpoints. If master seed is compromised, the server would need to be redeployed but because it’s stateless, and doesn’t store any critical information they is nothing to worry about.