Elliptic Curve VRF
Implementations of Verifiable Random Function with Additional Data (VRF-AD) schemes built on a transcript-based Fiat-Shamir transform with support for multiple input/output pairs via delinearization.
Built on the Arkworks framework with
configurable cryptographic parameters and no_std support.
Supported Schemes
-
Tiny VRF: Compact proof. Loosely inspired by RFC-9381, adapted with a transcript-based Fiat-Shamir transform, support for additional data, and multiple I/O pairs via delinearization.
-
Thin VRF: Same structure as Tiny VRF but stores the nonce commitment instead of the challenge, enabling batch verification at the cost of a slightly larger proof.
-
Pedersen VRF: Key-hiding VRF based on the construction introduced by BCHSV23. Replaces the public key with a Pedersen commitment to the secret key, serving as a building block for anonymized ring signatures.
-
Ring VRF: Anonymized ring VRF combining Pedersen VRF with the ring proof scheme derived from CSSV22. Proves that a single blinded key is a member of a committed ring without revealing which one.
Specifications
Built-In Suites
The library conditionally includes the following pre-configured suites (see features section):
- Ed25519: Supports Tiny, Thin, and Pedersen VRF.
- Secp256r1: Supports Tiny, Thin, and Pedersen VRF.
- Bandersnatch (Edwards curve on BLS12-381): Supports Tiny, Thin, Pedersen, and Ring VRF.
- JubJub (Edwards curve on BLS12-381): Supports Tiny, Thin, Pedersen, and Ring VRF.
- Baby-JubJub (Edwards curve on BN254): Supports Tiny, Thin, Pedersen, and Ring VRF.
Basic Usage
use *;
// Create a secret key from a seed
let secret = from_seed;
// Derive the corresponding public key
let public = secret.public;
// Create an input by hashing data to a curve point
let input = new.unwrap;
// Compute the VRF output (gamma point)
let output = secret.output;
// Get a deterministic hash from the VRF output point
let hash_bytes = output.hash;
Tiny VRF
Compact VRF-AD producing a short (c, s) proof.
Prove
use Prover;
let io = secret.vrf_io;
// Generate a proof that binds the input-output pair and auxiliary data
let proof = secret.prove;
Verify
use Verifier;
// Verify the proof against the public key
let result = public.verify;
assert!;
Thin-VRF
The Thin VRF merges the public-key Schnorr pair and the VRF I/O pair into a single DLEQ relation via delinearization, then proves it with a Schnorr-like proof (R, s).
Prove
use Prover;
let io = secret.vrf_io;
let proof = secret.prove;
Verify
use Verifier;
let result = public.verify;
assert!;
Batch verify
use ;
let proof1 = secret.prove;
let proof2 = secret.prove;
let mut batch = new;
batch.push;
batch.push;
assert!;
Pedersen-VRF
Key-hiding VRF that replaces the public key with a Pedersen commitment to the secret key.
Prove
use Prover;
let io = secret.vrf_io;
// Generate a proof with a blinding factor
let = secret.prove;
// The proof includes a commitment to the public key
let key_commitment = proof.key_commitment;
Verify
use Verifier;
// Verify without knowing which specific public key was used.
// Verifies that the secret key used to generate `output` is the same as
// the secret key used to generate `proof.key_commitment()`.
let result = verify;
assert!;
// Verify the proof was created using a specific public key.
// This requires knowledge of the blinding factor.
let expected = .into_affine;
assert_eq!;
Ring-VRF
The Ring VRF provides anonymity within a set of public keys using zero-knowledge proofs.
Ring construction
const RING_SIZE: usize = 100;
let prover_key_index = 3;
// Construct an example ring with dummy keys
let mut ring =
.map
.;
// Patch the ring with the public key of the prover
ring = public.0;
// Any key can be replaced with the padding point
ring = padding_point;
// Create parameters for the ring proof system.
// These parameters are reusable across multiple proofs.
let params = from_seed;
Prove
use Prover;
// Create a prover key specific to this ring
let prover_key = params.prover_key.unwrap;
// Create a prover instance for the specific position in the ring
let prover = params.prover;
let io = secret.vrf_io;
// Generate a zero-knowledge proof that:
// 1. The prover knows a secret key for one of the public keys in the ring
// 2. That secret key was used to generate the VRF output
let proof = secret.prove;
Verify
use Verifier;
// Create a verifier key for this ring
let verifier_key = params.verifier_key.unwrap;
// Create a verifier instance
let verifier = params.verifier;
// Verify the proof - this confirms that:
// 1. The proof was created by someone who knows a secret key in the ring
// 2. The VRF output is correct for the given input
// But it does NOT reveal which ring member created the proof
let result = verify;
Verifier key from commitment
// For efficiency, a commitment to the ring can be shared
let ring_commitment = params.verifier_key.unwrap.commitment;
// A verifier can reconstruct the verifier key from just the commitment
// without needing the full ring of public keys
let verifier_key = params.verifier_key_from_commitment;
Features
default:stdfull: Enables all features listed below exceptsecret-split,parallel,asm,test-vectors.secret-split: Split-secret scalar multiplication. Secret scalar is split into the sum of two scalars, which randomly mutate but retain the same sum. Incurs 2x penalty in some internal sensible scalar multiplications, but provides side channel defenses.ring: Ring-VRF for the curves supporting it.test-vectors: Deterministic ring-vrf proof. Useful for reproducible test vectors generation.
Curves
ed25519jubjubbandersnatchbaby-jubjubsecp256r1
Arkworks optimizations
parallel: Parallel execution where worth usingrayon.asm: Assembly implementation of some low level operations.
License
Distributed under the MIT License.