Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ members = [
"schnorr_fun",
"ecdsa_fun",
"sigma_fun",
"arithmetic_macros"
"arithmetic_macros",
"vrf_fun"
]
resolver = "2"

Expand Down
2 changes: 1 addition & 1 deletion schnorr_fun/src/frost/share.rs
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,7 @@ mod share_backup {
if share_index_u32 >= HUMAN_READABLE_THRESHOLD {
prop_assert!(backup.starts_with("frost1"));
} else {
assert!(backup.starts_with(&format!("frost[{}]", share_index_u32)));
assert!(backup.starts_with(&format!("frost[{share_index_u32}]")));
}

prop_assert_eq!(SecretShare::from_bech32_backup(&backup), Ok(secret_share))
Expand Down
12 changes: 4 additions & 8 deletions secp256kfun/tests/test_hash_to_curve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,12 @@ fn test_hash_to_curve_sswu_test_vectors() {
assert_eq!(
&x_bytes[..],
&expected_x[..],
"Test vector {} failed: x coordinate mismatch",
i
"Test vector {i} failed: x coordinate mismatch",
);
assert_eq!(
&y_bytes[..],
&expected_y[..],
"Test vector {} failed: y coordinate mismatch",
i
"Test vector {i} failed: y coordinate mismatch",
);
}
}
Expand Down Expand Up @@ -149,8 +147,7 @@ fn test_hash_to_curve_test_vectors() {

assert_eq!(
actual_bytes, expected_bytes,
"Test vector {} failed: msg={:?}",
i, msg
"Test vector {i} failed: msg={msg:?}",
);

// Verify determinism
Expand Down Expand Up @@ -209,8 +206,7 @@ fn test_hash_to_curve_rfc9381_tai() {

assert_eq!(
actual_bytes, expected_bytes,
"Test vector {} failed: msg={:?}, salt={:?}",
i, msg, salt
"Test vector {i} failed: msg={msg:?}, salt={salt:?}",
);

// Verify determinism
Expand Down
25 changes: 25 additions & 0 deletions vrf_fun/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[package]
name = "vrf_fun"
version = "0.11.0"
edition = "2024"
rust-version = "1.85.0"

[features]
default = ["std"]
std = ["secp256kfun/std"]
serde = ["dep:serde", "secp256kfun/serde", "sigma_fun/serde", "generic-array/serde"]
bincode = ["secp256kfun/bincode", "sigma_fun/bincode", "dep:bincode"]

[dependencies]
secp256kfun = { path = "../secp256kfun", default-features = false }
sigma_fun = { path = "../sigma_fun", default-features = false, features = ["secp256k1"] }
rand_chacha = { version = "0.3", default-features = false }
serde = { version = "1.0", optional = true, default-features = false, features = ["derive"] }
generic-array = { version = "0.14", default-features = false }
bincode = { workspace = true, optional = true }

[dev-dependencies]
rand = "0.8"
sha2 = "0.10"
proptest = "1.0"
bincode = { workspace = true }
122 changes: 122 additions & 0 deletions vrf_fun/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# vrf_fun

Verifiable Random Function (VRF) implementation for secp256k1.

## Overview

This crate provides RFC 9381 compliant VRF implementations for secp256k1, supporting both:
- **TAI (Try-And-Increment)** hash-to-curve method
- **RFC 9380** hash-to-curve method

## Features

- RFC 9381 compliant VRF implementation
- Support for both TAI and RFC 9380 hash-to-curve methods
- Simple VRF variant for when spec compliance is not required
- Generic over hash functions (SHA-256, etc.)
- Deterministic proofs
- Suite strings: `0xFE` for TAI, `0xFF` for RFC SSWU

## Usage

### High-Level API

#### RFC 9381 with TAI (Try-And-Increment)

```rust
use secp256kfun::{prelude::*, KeyPair};
use vrf_fun::rfc9381;

// Generate a keypair
let keypair = KeyPair::new(Scalar::random(&mut rand::thread_rng()));

// Create a VRF proof
let alpha = b"test message";
let proof = rfc9381::tai::prove::<sha2::Sha256>(&keypair, alpha);

// Verify the proof
let verified = rfc9381::tai::verify::<sha2::Sha256>(
keypair.public_key(),
alpha,
&proof
).expect("proof should verify");

// Get the VRF output
let beta = verified.rfc9381_output::<sha2::Sha256>();
```

#### RFC 9381 with RFC 9380 Hash-to-Curve

```rust
use vrf_fun::rfc9381;

// Same keypair and message
let proof = rfc9381::sswu::prove::<sha2::Sha256>(&keypair, alpha);

// Verify with the RFC 9380 verifier
let verified = rfc9381::sswu::verify::<sha2::Sha256>(
keypair.public_key(),
alpha,
&proof
).expect("proof should verify");

let beta = verified.rfc9381_sswu_output::<sha2::Sha256>();
```

### Low-Level API

For more control over the hash-to-curve process:

```rust
use vrf_fun::{rfc9381::Rfc9381TaiVrf, SimpleVrf};
use secp256kfun::{prelude::*, KeyPair};

// Create VRF instance
let vrf = Rfc9381TaiVrf::<sha2::Sha256>::default();

// Hash to curve yourself
let h = Point::hash_to_curve_rfc9381_tai::<sha2::Sha256>(alpha, b"");

// Generate proof
let proof = vrf.prove(&keypair, h);

// Verify
let verified = vrf.verify(keypair.public_key(), h, &proof)
.expect("proof should verify");
```

## Implementation Details

### Challenge Generation

The challenge is computed as:
```
c = Hash(suite_string || 0x02 || Y || H || Gamma || U || V || 0x00)
```

Where:
- `suite_string`: `0xFE` for TAI, `0xFF` for RFC 9380
- `Y` is the public key
- `H` is the hash-to-curve of the input
- `Gamma` is the VRF output point (x*H)
- `U` and `V` are the DLEQ proof commitments

The hash output is truncated to 16 bytes for secp256k1.

### VRF Output

The VRF output beta is computed as:
```
beta = Hash(suite_string || 0x03 || Gamma || 0x00)
```

## Important Notes

- The TAI and RFC 9380 variants use different suite strings (0xFE and 0xFF)
- Proofs generated with one method cannot be verified with the other
- The same input will produce different outputs with different hash-to-curve methods
- This implementation includes the public key in the challenge (unlike draft-05)

## Generic Hash Support

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.
30 changes: 30 additions & 0 deletions vrf_fun/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//! Verifiable Random Function (VRF) implementations for secp256k1.

#![no_std]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]

#[cfg(feature = "std")]
extern crate std;

pub mod rfc9381;
pub mod vrf;

pub use vrf::{VerifiedRandomOutput, Vrf, VrfProof};

use rand_chacha::ChaCha20Rng;
use sigma_fun::{
Eq, HashTranscript,
generic_array::typenum::U32,
secp256k1::{DL, DLG},
};

/// Type alias for the DLEQ proof with configurable challenge length
pub type VrfDleq<ChallengeLength> = Eq<DLG<ChallengeLength>, DL<ChallengeLength>>;

/// Simple VRF using HashTranscript with 32-byte challenges
pub type SimpleVrf<H> = Vrf<HashTranscript<H, ChaCha20Rng>, U32>;

/// Re-export the [RFC 9381] type aliases
///
/// [RFC 9381]: https://datatracker.ietf.org/doc/html/rfc9381
pub use rfc9381::{Rfc9381SswuVrf, Rfc9381TaiVrf};
Loading
Loading