ark_vrf/lib.rs
1//! # Elliptic Curve VRF-AD
2//!
3//! Implementations of Verifiable Random Functions with Additional Data (VRF-AD)
4//! based on elliptic curve cryptography. Built on the [Arkworks](https://github.com/arkworks-rs)
5//! framework with configurable cryptographic parameters.
6//!
7//! VRF-AD extends standard VRF constructions by binding auxiliary data to the proof,
8//! providing stronger contextual security guarantees.
9//!
10//! ## Schemes
11//!
12//! - **IETF VRF**: ECVRF implementation compliant with [RFC9381](https://datatracker.ietf.org/doc/rfc9381)
13//!
14//! - **Pedersen VRF**: Key-hiding VRF using Pedersen commitments as described in
15//! [BCHSV23](https://eprint.iacr.org/2023/002)
16//!
17//! - **Ring VRF**: Zero-knowledge VRF with signer anonymity within a key set, based on
18//! [BCHSV23](https://eprint.iacr.org/2023/002)
19//!
20//! ### Specifications
21//!
22//! - [VRF Schemes](https://github.com/davxy/bandersnatch-vrf-spec)
23//! - [Ring Proof](https://github.com/davxy/ring-proof-spec)
24//!
25//! ## Built-In suites
26//!
27//! The library conditionally includes the following pre-configured suites (see features section):
28//!
29//! - **Ed25519-SHA-512-TAI**: Supports IETF and Pedersen VRF.
30//! - **Secp256r1-SHA-256-TAI**: Supports IETF and Pedersen VRF.
31//! - **Bandersnatch** (_Edwards curve on BLS12-381_): Supports IETF, Pedersen, and Ring VRF.
32//! - **JubJub** (_Edwards curve on BLS12-381_): Supports IETF, Pedersen, and Ring VRF.
33//! - **Baby-JubJub** (_Edwards curve on BN254_): Supports IETF, Pedersen, and Ring VRF.
34//!
35//! ## Usage
36//!
37//! ```rust,ignore
38//! use ark_vrf::suites::bandersnatch::*;
39//!
40//! let secret = Secret::from_seed(b"example seed");
41//! let public = secret.public();
42//! let input = Input::new(b"example input").unwrap();
43//! let output = secret.output(input);
44//! let hash_bytes = output.hash();
45//! ```
46//!
47//! ### Proof Generation Schemes
48//!
49//! - [ietf] vrf proof
50//! - [pedersen] vrf proof
51//! - [ring] vrf proof
52//!
53//! ## Features
54//!
55//! - `default`: `std`
56//! - `full`: Enables all features listed below except `secret-split`, `parallel`, `asm`, `rfc-6979`, `test-vectors`.
57//! - `secret-split`: Point scalar multiplication with secret split. Secret scalar is split into the sum
58//! of two scalars, which randomly mutate but retain the same sum. Incurs 2x penalty in some internal
59//! sensible scalar multiplications, but provides side channel defenses.
60//! - `ring`: Ring-VRF for the curves supporting it.
61//! - `rfc-6979`: Support for nonce generation according to RFC-9381 section 5.4.2.1.
62//! - `test-vectors`: Deterministic ring-vrf proof. Useful for reproducible test vectors generation.
63//!
64//! ### Curves
65//!
66//! - `ed25519`
67//! - `jubjub`
68//! - `bandersnatch`
69//! - `baby-jubjub`
70//! - `secp256r1`
71//!
72//! ### Arkworks optimizations
73//!
74//! - `parallel`: Parallel execution where worth using `rayon`.
75//! - `asm`: Assembly implementation of some low level operations.
76//!
77//! ## License
78//!
79//! Distributed under the [MIT License](./LICENSE).
80
81#![cfg_attr(not(feature = "std"), no_std)]
82#![deny(unsafe_code)]
83
84use ark_ec::{AffineRepr, CurveGroup};
85use ark_ff::{PrimeField, Zero};
86use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
87use ark_std::vec::Vec;
88
89use digest::Digest;
90use zeroize::Zeroize;
91
92pub mod codec;
93pub mod ietf;
94pub mod pedersen;
95pub mod suites;
96pub mod utils;
97
98#[cfg(feature = "ring")]
99pub mod ring;
100
101#[cfg(test)]
102mod testing;
103
104/// Re-export stuff that may be useful downstream.
105pub mod reexports {
106 pub use ark_ec;
107 pub use ark_ff;
108 pub use ark_serialize;
109 pub use ark_std;
110}
111
112use codec::Codec;
113
114/// Suite's affine curve point type.
115pub type AffinePoint<S> = <S as Suite>::Affine;
116/// Suite's base field type.
117pub type BaseField<S> = <AffinePoint<S> as AffineRepr>::BaseField;
118/// Suite's scalar field type.
119pub type ScalarField<S> = <AffinePoint<S> as AffineRepr>::ScalarField;
120/// Suite's curve configuration type.
121pub type CurveConfig<S> = <AffinePoint<S> as AffineRepr>::Config;
122
123/// Suite's hash output type.
124pub type HashOutput<S> = digest::Output<<S as Suite>::Hasher>;
125
126/// Overarching errors.
127#[derive(Debug)]
128pub enum Error {
129 /// Verification error
130 VerificationFailure,
131 /// Bad input data
132 InvalidData,
133}
134
135impl From<ark_serialize::SerializationError> for Error {
136 fn from(_err: ark_serialize::SerializationError) -> Self {
137 Error::InvalidData
138 }
139}
140
141/// Defines a cipher suite.
142///
143/// This trait can be used to easily implement a VRF which follows the guidelines
144/// given by RFC-9381 section 5.5.
145///
146/// Can be easily customized to implement more exotic VRF types by overwriting
147/// the default methods implementations.
148pub trait Suite: Copy {
149 /// Suite identifier (aka `suite_string` in RFC-9381)
150 const SUITE_ID: &'static [u8];
151
152 /// Challenge encoded length.
153 ///
154 /// Must be at least equal to the Hash length.
155 const CHALLENGE_LEN: usize;
156
157 /// Curve point in affine representation.
158 ///
159 /// The point is guaranteed to be in the correct prime order subgroup
160 /// by the `AffineRepr` bound.
161 type Affine: AffineRepr;
162
163 /// Overarching hasher.
164 ///
165 /// Used wherever an hash is required: nonce, challenge, MAC, etc.
166 type Hasher: Digest + Clone;
167
168 /// Overarching codec.
169 ///
170 /// Used wherever we need to encode/decode points and scalars.
171 type Codec: codec::Codec<Self>;
172
173 /// Nonce generation as described by RFC-9381 section 5.4.2.
174 ///
175 /// The default implementation provides the variant described
176 /// by section 5.4.2.2 of RFC-9381 which in turn is a derived
177 /// from steps 2 and 3 in section 5.1.6 of
178 /// [RFC8032](https://tools.ietf.org/html/rfc8032).
179 ///
180 /// The algorithm generate the nonce value in a deterministic
181 /// pseudorandom fashion.
182 ///
183 /// `Hasher` output **MUST** be be at least 64 bytes.
184 ///
185 /// # Panics
186 ///
187 /// This function panics if `Hasher` output is less than 64 bytes.
188 #[inline(always)]
189 fn nonce(sk: &ScalarField<Self>, pt: Input<Self>, ad: &[u8]) -> ScalarField<Self> {
190 utils::nonce_rfc_8032::<Self>(sk, &pt.0, ad)
191 }
192
193 /// Challenge generation as described by RCF-9381 section 5.4.3.
194 ///
195 /// Hashes several points on the curve.
196 ///
197 /// This implementation extends the RFC procedure to allow adding
198 /// some optional additional data too the hashing procedure.
199 #[inline(always)]
200 fn challenge(pts: &[&AffinePoint<Self>], ad: &[u8]) -> ScalarField<Self> {
201 utils::challenge_rfc_9381::<Self>(pts, ad)
202 }
203
204 /// Hash data to a curve point.
205 ///
206 /// By default uses "try and increment" method described by RFC-9381.
207 ///
208 /// The input `data` is assumed to be `[salt||]alpha` according to the RFC-9381.
209 /// In other words, salt is not applied by this function.
210 #[inline(always)]
211 fn data_to_point(data: &[u8]) -> Option<AffinePoint<Self>> {
212 utils::hash_to_curve_tai_rfc_9381::<Self>(data)
213 }
214
215 /// Map the point to a hash value using `Self::Hasher`.
216 ///
217 /// By default uses the algorithm described by RFC-9381 without cofactor clearing.
218 #[inline(always)]
219 fn point_to_hash(pt: &AffinePoint<Self>) -> HashOutput<Self> {
220 utils::point_to_hash_rfc_9381::<Self>(pt, false)
221 }
222
223 /// Generator used through all the suite.
224 ///
225 /// Defaults to Arkworks provided generator.
226 #[inline(always)]
227 fn generator() -> AffinePoint<Self> {
228 Self::Affine::generator()
229 }
230}
231
232/// Secret key for VRF operations.
233///
234/// Contains the private scalar and cached public key.
235/// Implements automatic zeroization on drop.
236#[derive(Debug, Clone, PartialEq)]
237pub struct Secret<S: Suite> {
238 /// Secret scalar.
239 pub(crate) scalar: ScalarField<S>,
240 /// Cached public key.
241 pub(crate) public: Public<S>,
242}
243
244impl<S: Suite> Drop for Secret<S> {
245 fn drop(&mut self) {
246 self.scalar.zeroize()
247 }
248}
249
250impl<S: Suite> CanonicalSerialize for Secret<S> {
251 fn serialize_with_mode<W: ark_std::io::prelude::Write>(
252 &self,
253 writer: W,
254 compress: ark_serialize::Compress,
255 ) -> Result<(), ark_serialize::SerializationError> {
256 self.scalar.serialize_with_mode(writer, compress)
257 }
258
259 fn serialized_size(&self, compress: ark_serialize::Compress) -> usize {
260 self.scalar.serialized_size(compress)
261 }
262}
263
264impl<S: Suite> CanonicalDeserialize for Secret<S> {
265 fn deserialize_with_mode<R: ark_std::io::prelude::Read>(
266 reader: R,
267 compress: ark_serialize::Compress,
268 validate: ark_serialize::Validate,
269 ) -> Result<Self, ark_serialize::SerializationError> {
270 let scalar = <ScalarField<S> as CanonicalDeserialize>::deserialize_with_mode(
271 reader, compress, validate,
272 )?;
273 Ok(Self::from_scalar(scalar))
274 }
275}
276
277impl<S: Suite> ark_serialize::Valid for Secret<S> {
278 fn check(&self) -> Result<(), ark_serialize::SerializationError> {
279 self.scalar.check()
280 }
281}
282
283impl<S: Suite> Secret<S> {
284 /// Construct a `Secret` from the given scalar.
285 pub fn from_scalar(scalar: ScalarField<S>) -> Self {
286 let public = Public((S::generator() * scalar).into_affine());
287 Self { scalar, public }
288 }
289
290 /// Derives a `Secret` scalar deterministically from a seed.
291 ///
292 /// The seed is hashed using `Suite::Hasher`, and the output is reduced
293 /// modulo the curve's order to produce a valid scalar in the range
294 /// `[1, n - 1]`. No clamping or multiplication by the cofactor is
295 /// performed, regardless of the curve.
296 ///
297 /// The caller is responsible for ensuring that the resulting scalar is
298 /// used safely with respect to the target curve's cofactor and subgroup
299 /// properties.
300 pub fn from_seed(seed: &[u8]) -> Self {
301 let mut cnt = 0_u8;
302 let scalar = loop {
303 let mut hasher = S::Hasher::new();
304 hasher.update(seed);
305 if cnt > 0 {
306 hasher.update([cnt]);
307 }
308 let bytes = hasher.finalize();
309 let scalar = ScalarField::<S>::from_le_bytes_mod_order(&bytes[..]);
310 if !scalar.is_zero() {
311 break scalar;
312 }
313 cnt += 1;
314 };
315 Self::from_scalar(scalar)
316 }
317
318 /// Construct an ephemeral `Secret` using the provided randomness source.
319 pub fn from_rand(rng: &mut impl ark_std::rand::RngCore) -> Self {
320 let mut seed = [0u8; 32];
321 rng.fill_bytes(&mut seed);
322 Self::from_seed(&seed)
323 }
324
325 /// Get the secret scalar.
326 pub fn scalar(&self) -> &ScalarField<S> {
327 &self.scalar
328 }
329
330 /// Get the associated public key.
331 pub fn public(&self) -> Public<S> {
332 self.public
333 }
334
335 /// Get the VRF output point relative to input.
336 pub fn output(&self, input: Input<S>) -> Output<S> {
337 Output(smul!(input.0, self.scalar).into_affine())
338 }
339}
340
341/// Public key generic over the cipher suite.
342///
343/// Elliptic curve point representing the public component of a VRF key pair.
344#[derive(Debug, Copy, Clone, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
345pub struct Public<S: Suite>(pub AffinePoint<S>);
346
347impl<S: Suite> Public<S> {
348 /// Construct from an affine point.
349 pub fn from_affine(value: AffinePoint<S>) -> Self {
350 Self(value)
351 }
352}
353
354/// VRF input point generic over the cipher suite.
355///
356/// Elliptic curve point representing the VRF input.
357#[derive(Debug, Clone, Copy, PartialEq, Eq, CanonicalSerialize, CanonicalDeserialize)]
358pub struct Input<S: Suite>(pub AffinePoint<S>);
359
360impl<S: Suite> Input<S> {
361 /// Construct from [`Suite::data_to_point`].
362 ///
363 /// Maps arbitrary data to a curve point via hash-to-curve.
364 pub fn new(data: &[u8]) -> Option<Self> {
365 S::data_to_point(data).map(Input)
366 }
367}
368
369impl<S: Suite> Input<S> {
370 /// Construct from an affine point.
371 pub fn from_affine(value: AffinePoint<S>) -> Self {
372 Self(value)
373 }
374}
375
376/// VRF output point generic over the cipher suite.
377///
378/// Elliptic curve point representing the VRF output.
379#[derive(Debug, Clone, Copy, PartialEq, Eq, CanonicalSerialize, CanonicalDeserialize)]
380pub struct Output<S: Suite>(pub AffinePoint<S>);
381
382impl<S: Suite> Output<S> {
383 /// Construct from an affine point.
384 pub fn from_affine(value: AffinePoint<S>) -> Self {
385 Self(value)
386 }
387}
388
389impl<S: Suite> Output<S> {
390 /// Hash the output point to a deterministic byte string.
391 pub fn hash(&self) -> HashOutput<S> {
392 S::point_to_hash(&self.0)
393 }
394}
395
396/// Type aliases for the given suite.
397#[macro_export]
398macro_rules! suite_types {
399 ($suite:ident) => {
400 #[allow(dead_code)]
401 pub type Secret = $crate::Secret<$suite>;
402 #[allow(dead_code)]
403 pub type Public = $crate::Public<$suite>;
404 #[allow(dead_code)]
405 pub type Input = $crate::Input<$suite>;
406 #[allow(dead_code)]
407 pub type Output = $crate::Output<$suite>;
408 #[allow(dead_code)]
409 pub type AffinePoint = $crate::AffinePoint<$suite>;
410 #[allow(dead_code)]
411 pub type ScalarField = $crate::ScalarField<$suite>;
412 #[allow(dead_code)]
413 pub type BaseField = $crate::BaseField<$suite>;
414 #[allow(dead_code)]
415 pub type IetfProof = $crate::ietf::Proof<$suite>;
416 #[allow(dead_code)]
417 pub type PedersenProof = $crate::pedersen::Proof<$suite>;
418 #[allow(dead_code)]
419 pub type PedersenBatchItem = $crate::pedersen::BatchItem<$suite>;
420 #[allow(dead_code)]
421 pub type PedersenBatchVerifier = $crate::pedersen::BatchVerifier<$suite>;
422 };
423}
424
425#[cfg(test)]
426mod tests {
427 use super::*;
428 use suites::testing::{Input, Secret};
429 use testing::{TEST_SEED, random_val};
430
431 #[test]
432 fn vrf_output_check() {
433 use ark_std::rand::SeedableRng;
434 let mut rng = rand_chacha::ChaCha20Rng::from_seed([42; 32]);
435 let secret = Secret::from_seed(TEST_SEED);
436 let input = Input::from_affine(random_val(Some(&mut rng)));
437 let output = secret.output(input);
438
439 let expected = "71c1b2ee6e46c59e3bd0e2f0e2852b90ab56abb223180b00bd6c8ec6b11af18c";
440 assert_eq!(expected, hex::encode(output.hash()));
441 }
442}