Skip to content

Commit bc3ca5b

Browse files
authored
Merge pull request #223 from LLFourn/vrf_fun
feat: Add vrf_fun crate
2 parents d7ad294 + 8b95e94 commit bc3ca5b

File tree

10 files changed

+1033
-10
lines changed

10 files changed

+1033
-10
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

schnorr_fun/src/frost/share.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,7 @@ mod share_backup {
489489
if share_index_u32 >= HUMAN_READABLE_THRESHOLD {
490490
prop_assert!(backup.starts_with("frost1"));
491491
} else {
492-
assert!(backup.starts_with(&format!("frost[{}]", share_index_u32)));
492+
assert!(backup.starts_with(&format!("frost[{share_index_u32}]")));
493493
}
494494

495495
prop_assert_eq!(SecretShare::from_bech32_backup(&backup), Ok(secret_share))

secp256kfun/tests/test_hash_to_curve.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,12 @@ fn test_hash_to_curve_sswu_test_vectors() {
4444
assert_eq!(
4545
&x_bytes[..],
4646
&expected_x[..],
47-
"Test vector {} failed: x coordinate mismatch",
48-
i
47+
"Test vector {i} failed: x coordinate mismatch",
4948
);
5049
assert_eq!(
5150
&y_bytes[..],
5251
&expected_y[..],
53-
"Test vector {} failed: y coordinate mismatch",
54-
i
52+
"Test vector {i} failed: y coordinate mismatch",
5553
);
5654
}
5755
}
@@ -149,8 +147,7 @@ fn test_hash_to_curve_test_vectors() {
149147

150148
assert_eq!(
151149
actual_bytes, expected_bytes,
152-
"Test vector {} failed: msg={:?}",
153-
i, msg
150+
"Test vector {i} failed: msg={msg:?}",
154151
);
155152

156153
// Verify determinism
@@ -209,8 +206,7 @@ fn test_hash_to_curve_rfc9381_tai() {
209206

210207
assert_eq!(
211208
actual_bytes, expected_bytes,
212-
"Test vector {} failed: msg={:?}, salt={:?}",
213-
i, msg, salt
209+
"Test vector {i} failed: msg={msg:?}, salt={salt:?}",
214210
);
215211

216212
// Verify determinism

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)