Retroactive Grant Application : Blindvault

Blindvault - Privacy-Preserving Credentials Issuance Service

Ask: $17,000 USD
FPF issue: Retroactive Grant Application - Blindvault · Issue #39 · Financial-Privacy-Foundation/ZcashCoinholderGrantsProgram · GitHub
GitHub: GitHub - rawbytedev/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 · GitHub
Release(v1.3.0): Release v1.3.0: fix: included utils.ts in the connfig · rawbytedev/blindvault · GitHub
Binaries: Windows, Linux(Server and client), Js(client)
License: MIT

Why it exists

Zcash enables private transactions between people, but still lacks the tools for private identity and credentials verification.

Most of the time when it is needed to distribute ZEC to a group of people, prevent sybil attacks or issue access to private event, you have to build complex cryptography infrastructure or tool from scratch. And developers would certainly prefer a simpler approach such as centralized database which stores user details, this does not align at all with privacy.

Blindvault mainly serves to solve that problem. It allows an application to ensure eligible users, can perform actions in private without their identity being exposed(private community voting). Making use of BLS12-381 blind signatures and DLEQ proof, blindvault issue a credential to a user and then be redeemed later by the user without the server linking the initial issuance to the redemption request.

Blindvault enables private interactions, without ever exposing or knowing a user identity therefore preserving their privacy

What It Does

Blindvault does 3 things:

  • Issuance: when the client request a credential, Blindvault signs it blindly using BLS12-381.
  • Verification: Blindvault attach a DLEQ proof to every issuance, ensuring the issuance was signed with the correct key. This prevent malicious servers from issuing fake credentials.
  • Redemption: The client redeems the credential sending over the unblinded signature, the server after ensuring it belongs to the appropriate class and epoch verify that the signature is valid, before computing a nullifier(deterministic hash). The server checks Redis to ensure that the credential was never used before. This allow granting access or recording a vote without knowing who asked for the credential in the first place.

Blindvault provide classes and Epoch which allow for keys rotation.

Values to Zcash

Blindvault has many practical uses for the zcash community, and that’s precisely why I’ve built it as an infrastructure service.
For pratical use cases:

  • Private Governance: Allow voting throughout the zcash community, without having to compromise the voter privacy, each eligible user is given a credentials that can be used later for the vote.
  • Community Users Privacy: Users can prove their hold a specific status(“admin”, “moderator”) without revealing their forum username

Current 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
  • Epoch and class revocation

Future Features

  • Distributed Key Generation
  • Trusted‑dealer threshold
  • Threshold DLEQ proof generation
  • Batch Verification

I’ll be happy to answer any question and take any suggestion

I’ve started reviewing the code, mainly to identify issues that might came up, as of now the issues I was able to find are as follow:

  • When generating the DLEQ proof we didn’t create a copy for a scalar which is modified later, although the scalar is used once then no longer called throughout the process of proof generation, due to the modification debugging issues would become extremely hard
  • The key generation uses HKDF which derives from the master seed and other fields class, epoch, after we convert it directly into a scalar which might result in overflow or silent fails(very unlikely), so for fixing that I decided to used the built in hash to scalar function from the library.
  • When storing revocation in redis I made used of * wildcard but we use the same wildcard when listing all revocation so I decided to go with a simpler approach: once
    revoke:<class>:all is present all epochs of that class are marked as revoke, the behavior doesn’t change at all

I’ll make a new release with all fixes and issues sorted out this would allow all present features to be entirely safe and reliable to use.

I applied additional fixes and improvements:

  • Blindvault was not checking for context before heavy operation which my lead to ressource being consumed when the client disconnects or cancels requests, the newest commit fix that propagating the context and checking it before every cryptographic operation
  • A small fix to the key derivation which might create considerable issues later one, the previous derivation append strings together ProtocalTag + purpose + epoch + credential without any delimitation which can lead to collusion:
    • Epoch = "2026-01", Class = "test" → info = "BCISSIGNING_KEY2026-01test"
    • Epoch = "2026", Class = "-01test" → info = "BCISSIGNING_KEY2026-01test"

All those have been fixed

1 Like

I’ve started implementing memguard enclave to zero out sensitive data such as masterseed and passwords, the package was already created entirely and only needed to be integrated. It does aim to make blindvault more secure, with enclaves the byte of the master seed are only visible only when needed and can’t be viewed otherwise.

I’m essentially writing a thin wrapper over memguard GitHub - awnumar/memguard: Software sandbox for storage of sensitive information in memory. · GitHub as to only expose the functions that would be needed by blindvault

I’ve release v1.4.0 which solves all previous issues while providing solution to all critical issues and improvements:

CRITICAL FIXES and IMPROVEMENTS:

  • context propagation ensure resources are used meaningfully and operation are stopped when no longer needed
  • SecureMemory Enclaves added and fully implemented protecting the master seed in memory( currently they is still a small window at which the master seed is still visible that is during the enclave creation)
  • critical bug where the hex is used instead of the actually seed for key generation
  • bug related to validation, checks for hex lenght(64) not the actual bytes lenght(32)
  • bug related to rate limiting where limit.limit is nil and panics

PREVIOUS STATED ISSUES(fixed with this release):

  • DLEQ proof generation doesn’t copy before mutating(changes original values during generation of proof)
  • Conversion to scalar could potentially result to overflow
  • Wildcard used for revocation might trigger unexpected results in some cases
  • Context not fully propagate which result in wasted result when operation is cancelled by client
  • Key generation issue where carefully crafted credentials can pose as a legitimate one

Release v1.4.0 · rawbytedev/blindvault

Note: This version introduce huge changes which includes changes to key generation, this means that previous blindvault version credentials would not work with this new version

This is the first production-ready release. The cryptographic core, replay protection, revocation, and operational tooling have all been hardened and validated end-to-end.

  • BLS12-381 blind signatures with DLEQ proofs so clients can verify the server used the correct derived signing key.
  • Deterministic key derivation from a single master seed (HKDF-SHA256), scoped by epoch and credential class for cryptographic namespace isolation.
  • Replay protection using deterministic nullifiers backed by Redis (or an in-memory store for development).
  • Credential revocation at the class or class+epoch level, with optional auto-expiring revocations.
  • JWT authentication with role separation between issuer and admin.
  • Prometheus metrics and structured JSON logging with request IDs.
  • Go client library and CLI (bv) for the full blind → issue → verify → unblind → redeem flow.
  • Hardened rate limiter with X-Forwarded-For trust restricted to private/loopback peers and bounded client tracking.

IMPROVEMENTS:

  • Master seed is sealed in a memguard enclave and the inline hex is cleared after load.
  • Scalar deserialization now rejects out-of-range values.
  • Rate limiter IP parsing uses net.SplitHostPort and only trusts forwarded headers from loopback/private peers.
  • request bodies are capped at 1 MiB via http.MaxBytesReader.
  • Production config validation now rejects inline YAML seeds, memory stores, demo endpoints, short secrets, and default placeholder values.

BREAKING CHANGES:

  • MASTER_SEED_HEX is no longer read. Use BLINDVAULT_MASTER_SEED_HEX or BLINDVAULT_SEED_FILE.
  • YAML field renamed to blindvault_master_seed_hex (from master_seed_hex).
  • MAX_CLIENTS and REDIS_EXPIRATION are now required in production and must be greater than zero.
  • ActiveEpoch must be present in SupportedEpochs. The server will refuse to start otherwise.
  • POST /v1/credential/issue now requires the issuer role (or admin: true) on the JWT.
  • Production mode is stricter: use_demo and use_memory_store are forbidden, and jwt_issuer/jwt_audience are required

This version will serve as the foundation for future features:

  • Threshold DLEQ proof
  • Distributated Key Generation(DKG)
    and Improvements to the current code

Migration from v1.x:

  1. Rename master_seed_hex → blindvault_master_seed_hex in your YAML, or move the seed to BLINDVAULT_MASTER_SEED_HEX / BLINDVAULT_SEED_FILE.
  2. Add max_clients and redis_expiration to production configs.
  3. Ensure your JWTs include the issuer role (or admin: true) for issuance.
  4. Confirm active_epoch is listed in supported_epochs.

Full Changelog: Comparing v1.4.0...v2.0.0 · rawbytedev/blindvault · GitHub