Skip to content

Commit 4dac27d

Browse files
LLFournclaude
authored andcommitted
feat: Add vrf_fun crate
Add VRF (Verifiable Random Function) implementation for secp256k1: - Full RFC 9381 compliant implementation with both TAI and RFC 9380 SSWU methods - Simple VRF implementation for non-spec-compliant use cases - no_std support with optional std feature - Serde/bincode serialization support - Comprehensive test coverage with proptests and test vectors - MSRV: 1.85.0 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent d7ad294 commit 4dac27d

File tree

8 files changed

+1028
-1
lines changed

8 files changed

+1028
-1
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ members = [
55
"schnorr_fun",
66
"ecdsa_fun",
77
"sigma_fun",
8-
"arithmetic_macros"
8+
"arithmetic_macros",
9+
"vrf_fun"
910
]
1011
resolver = "2"
1112

vrf_fun/Cargo.toml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
[package]
2+
name = "vrf_fun"
3+
version = "0.11.0"
4+
edition = "2024"
5+
rust-version = "1.85.0"
6+
7+
[features]
8+
default = ["std"]
9+
std = ["secp256kfun/std"]
10+
serde = ["dep:serde", "secp256kfun/serde", "sigma_fun/serde", "generic-array/serde"]
11+
bincode = ["secp256kfun/bincode", "sigma_fun/bincode", "dep:bincode"]
12+
13+
[dependencies]
14+
secp256kfun = { path = "../secp256kfun", default-features = false }
15+
sigma_fun = { path = "../sigma_fun", default-features = false, features = ["secp256k1"] }
16+
rand_chacha = { version = "0.3", default-features = false }
17+
serde = { version = "1.0", optional = true, default-features = false, features = ["derive"] }
18+
generic-array = { version = "0.14", default-features = false }
19+
bincode = { workspace = true, optional = true }
20+
21+
[dev-dependencies]
22+
rand = "0.8"
23+
sha2 = "0.10"
24+
proptest = "1.0"
25+
bincode = { workspace = true }

vrf_fun/README.md

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# vrf_fun
2+
3+
Verifiable Random Function (VRF) implementation for secp256k1.
4+
5+
## Overview
6+
7+
This crate provides RFC 9381 compliant VRF implementations for secp256k1, supporting both:
8+
- **TAI (Try-And-Increment)** hash-to-curve method
9+
- **RFC 9380** hash-to-curve method
10+
11+
## Features
12+
13+
- RFC 9381 compliant VRF implementation
14+
- Support for both TAI and RFC 9380 hash-to-curve methods
15+
- Simple VRF variant for when spec compliance is not required
16+
- Generic over hash functions (SHA-256, etc.)
17+
- Deterministic proofs
18+
- Suite strings: `0xFE` for TAI, `0xFF` for RFC SSWU
19+
20+
## Usage
21+
22+
### High-Level API
23+
24+
#### RFC 9381 with TAI (Try-And-Increment)
25+
26+
```rust
27+
use secp256kfun::{prelude::*, KeyPair};
28+
use vrf_fun::rfc9381;
29+
30+
// Generate a keypair
31+
let keypair = KeyPair::new(Scalar::random(&mut rand::thread_rng()));
32+
33+
// Create a VRF proof
34+
let alpha = b"test message";
35+
let proof = rfc9381::tai::prove::<sha2::Sha256>(&keypair, alpha);
36+
37+
// Verify the proof
38+
let verified = rfc9381::tai::verify::<sha2::Sha256>(
39+
keypair.public_key(),
40+
alpha,
41+
&proof
42+
).expect("proof should verify");
43+
44+
// Get the VRF output
45+
let beta = verified.rfc9381_output::<sha2::Sha256>();
46+
```
47+
48+
#### RFC 9381 with RFC 9380 Hash-to-Curve
49+
50+
```rust
51+
use vrf_fun::rfc9381;
52+
53+
// Same keypair and message
54+
let proof = rfc9381::sswu::prove::<sha2::Sha256>(&keypair, alpha);
55+
56+
// Verify with the RFC 9380 verifier
57+
let verified = rfc9381::sswu::verify::<sha2::Sha256>(
58+
keypair.public_key(),
59+
alpha,
60+
&proof
61+
).expect("proof should verify");
62+
63+
let beta = verified.rfc9381_sswu_output::<sha2::Sha256>();
64+
```
65+
66+
### Low-Level API
67+
68+
For more control over the hash-to-curve process:
69+
70+
```rust
71+
use vrf_fun::{rfc9381::Rfc9381TaiVrf, SimpleVrf};
72+
use secp256kfun::{prelude::*, KeyPair};
73+
74+
// Create VRF instance
75+
let vrf = Rfc9381TaiVrf::<sha2::Sha256>::default();
76+
77+
// Hash to curve yourself
78+
let h = Point::hash_to_curve_rfc9381_tai::<sha2::Sha256>(alpha, b"");
79+
80+
// Generate proof
81+
let proof = vrf.prove(&keypair, h);
82+
83+
// Verify
84+
let verified = vrf.verify(keypair.public_key(), h, &proof)
85+
.expect("proof should verify");
86+
```
87+
88+
## Implementation Details
89+
90+
### Challenge Generation
91+
92+
The challenge is computed as:
93+
```
94+
c = Hash(suite_string || 0x02 || Y || H || Gamma || U || V || 0x00)
95+
```
96+
97+
Where:
98+
- `suite_string`: `0xFE` for TAI, `0xFF` for RFC 9380
99+
- `Y` is the public key
100+
- `H` is the hash-to-curve of the input
101+
- `Gamma` is the VRF output point (x*H)
102+
- `U` and `V` are the DLEQ proof commitments
103+
104+
The hash output is truncated to 16 bytes for secp256k1.
105+
106+
### VRF Output
107+
108+
The VRF output beta is computed as:
109+
```
110+
beta = Hash(suite_string || 0x03 || Gamma || 0x00)
111+
```
112+
113+
## Important Notes
114+
115+
- The TAI and RFC 9380 variants use different suite strings (0xFE and 0xFF)
116+
- Proofs generated with one method cannot be verified with the other
117+
- The same input will produce different outputs with different hash-to-curve methods
118+
- This implementation includes the public key in the challenge (unlike draft-05)
119+
120+
## Generic Hash Support
121+
122+
The implementation is generic over the hash function, constrained by `secp256kfun::hash::Hash32`. This allows using different SHA256 implementations or other 32-byte output hash functions.

vrf_fun/src/lib.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//! Verifiable Random Function (VRF) implementations for secp256k1.
2+
3+
#![no_std]
4+
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
5+
6+
#[cfg(feature = "std")]
7+
extern crate std;
8+
9+
pub mod rfc9381;
10+
pub mod vrf;
11+
12+
pub use vrf::{VerifiedRandomOutput, Vrf, VrfProof};
13+
14+
use rand_chacha::ChaCha20Rng;
15+
use sigma_fun::{
16+
Eq, HashTranscript,
17+
generic_array::typenum::U32,
18+
secp256k1::{DL, DLG},
19+
};
20+
21+
/// Type alias for the DLEQ proof with configurable challenge length
22+
pub type VrfDleq<ChallengeLength> = Eq<DLG<ChallengeLength>, DL<ChallengeLength>>;
23+
24+
/// Simple VRF using HashTranscript with 32-byte challenges
25+
pub type SimpleVrf<H> = Vrf<HashTranscript<H, ChaCha20Rng>, U32>;
26+
27+
/// Re-export the [RFC 9381] type aliases
28+
///
29+
/// [RFC 9381]: https://datatracker.ietf.org/doc/html/rfc9381
30+
pub use rfc9381::{Rfc9381SswuVrf, Rfc9381TaiVrf};

0 commit comments

Comments
 (0)