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
114pub type AffinePoint<S> = <S as Suite>::Affine;
115pub type BaseField<S> = <AffinePoint<S> as AffineRepr>::BaseField;
116pub type ScalarField<S> = <AffinePoint<S> as AffineRepr>::ScalarField;
117pub type CurveConfig<S> = <AffinePoint<S> as AffineRepr>::Config;
118
119pub type HashOutput<S> = digest::Output<<S as Suite>::Hasher>;
120
121/// Overarching errors.
122#[derive(Debug)]
123pub enum Error {
124    /// Verification error
125    VerificationFailure,
126    /// Bad input data
127    InvalidData,
128}
129
130impl From<ark_serialize::SerializationError> for Error {
131    fn from(_err: ark_serialize::SerializationError) -> Self {
132        Error::InvalidData
133    }
134}
135
136/// Defines a cipher suite.
137///
138/// This trait can be used to easily implement a VRF which follows the guidelines
139/// given by RFC-9381 section 5.5.
140///
141/// Can be easily customized to implement more exotic VRF types by overwriting
142/// the default methods implementations.
143pub trait Suite: Copy {
144    /// Suite identifier (aka `suite_string` in RFC-9381)
145    const SUITE_ID: &'static [u8];
146
147    /// Challenge encoded length.
148    ///
149    /// Must be at least equal to the Hash length.
150    const CHALLENGE_LEN: usize;
151
152    /// Curve point in affine representation.
153    ///
154    /// The point is guaranteed to be in the correct prime order subgroup
155    /// by the `AffineRepr` bound.
156    type Affine: AffineRepr;
157
158    /// Overarching hasher.
159    ///
160    /// Used wherever an hash is required: nonce, challenge, MAC, etc.
161    type Hasher: Digest;
162
163    /// Overarching codec.
164    ///
165    /// Used wherever we need to encode/decode points and scalars.
166    type Codec: codec::Codec<Self>;
167
168    /// Nonce generation as described by RFC-9381 section 5.4.2.
169    ///
170    /// The default implementation provides the variant described
171    /// by section 5.4.2.2 of RFC-9381 which in turn is a derived
172    /// from steps 2 and 3 in section 5.1.6 of
173    /// [RFC8032](https://tools.ietf.org/html/rfc8032).
174    ///
175    /// The algorithm generate the nonce value in a deterministic
176    /// pseudorandom fashion.
177    ///
178    /// `Hasher` output **MUST** be be at least 64 bytes.
179    ///
180    /// # Panics
181    ///
182    /// This function panics if `Hasher` output is less than 64 bytes.
183    #[inline(always)]
184    fn nonce(sk: &ScalarField<Self>, pt: Input<Self>) -> ScalarField<Self> {
185        utils::nonce_rfc_8032::<Self>(sk, &pt.0)
186    }
187
188    /// Challenge generation as described by RCF-9381 section 5.4.3.
189    ///
190    /// Hashes several points on the curve.
191    ///
192    /// This implementation extends the RFC procedure to allow adding
193    /// some optional additional data too the hashing procedure.
194    #[inline(always)]
195    fn challenge(pts: &[&AffinePoint<Self>], ad: &[u8]) -> ScalarField<Self> {
196        utils::challenge_rfc_9381::<Self>(pts, ad)
197    }
198
199    /// Hash data to a curve point.
200    ///
201    /// By default uses "try and increment" method described by RFC-9381.
202    ///
203    /// The input `data` is assumed to be `[salt||]alpha` according to the RFC-9381.
204    /// In other words, salt is not applied by this function.
205    #[inline(always)]
206    fn data_to_point(data: &[u8]) -> Option<AffinePoint<Self>> {
207        utils::hash_to_curve_tai_rfc_9381::<Self>(data)
208    }
209
210    /// Map the point to a hash value using `Self::Hasher`.
211    ///
212    /// By default uses the algorithm described by RFC-9381 without cofactor clearing.
213    #[inline(always)]
214    fn point_to_hash(pt: &AffinePoint<Self>) -> HashOutput<Self> {
215        utils::point_to_hash_rfc_9381::<Self>(pt, false)
216    }
217
218    /// Generator used through all the suite.
219    ///
220    /// Defaults to Arkworks provided generator.
221    #[inline(always)]
222    fn generator() -> AffinePoint<Self> {
223        Self::Affine::generator()
224    }
225}
226
227/// Secret key for VRF operations.
228///
229/// Contains the private scalar and cached public key.
230/// Implements automatic zeroization on drop.
231#[derive(Debug, Clone, PartialEq)]
232pub struct Secret<S: Suite> {
233    // Secret scalar.
234    pub scalar: ScalarField<S>,
235    // Cached public point.
236    pub public: Public<S>,
237}
238
239impl<S: Suite> Drop for Secret<S> {
240    fn drop(&mut self) {
241        self.scalar.zeroize()
242    }
243}
244
245impl<S: Suite> CanonicalSerialize for Secret<S> {
246    fn serialize_with_mode<W: ark_std::io::prelude::Write>(
247        &self,
248        writer: W,
249        compress: ark_serialize::Compress,
250    ) -> Result<(), ark_serialize::SerializationError> {
251        self.scalar.serialize_with_mode(writer, compress)
252    }
253
254    fn serialized_size(&self, compress: ark_serialize::Compress) -> usize {
255        self.scalar.serialized_size(compress)
256    }
257}
258
259impl<S: Suite> CanonicalDeserialize for Secret<S> {
260    fn deserialize_with_mode<R: ark_std::io::prelude::Read>(
261        reader: R,
262        compress: ark_serialize::Compress,
263        validate: ark_serialize::Validate,
264    ) -> Result<Self, ark_serialize::SerializationError> {
265        let scalar = <ScalarField<S> as CanonicalDeserialize>::deserialize_with_mode(
266            reader, compress, validate,
267        )?;
268        Ok(Self::from_scalar(scalar))
269    }
270}
271
272impl<S: Suite> ark_serialize::Valid for Secret<S> {
273    fn check(&self) -> Result<(), ark_serialize::SerializationError> {
274        self.scalar.check()
275    }
276}
277
278impl<S: Suite> Secret<S> {
279    /// Construct a `Secret` from the given scalar.
280    pub fn from_scalar(scalar: ScalarField<S>) -> Self {
281        let public = Public((S::generator() * scalar).into_affine());
282        Self { scalar, public }
283    }
284
285    /// Derives a `Secret` scalar deterministically from a seed.
286    ///
287    /// The seed is hashed using `Suite::Hasher`, and the output is reduced
288    /// modulo the curve's order to produce a valid scalar in the range
289    /// `[1, n - 1]`. No clamping or multiplication by the cofactor is
290    /// performed, regardless of the curve.
291    ///
292    /// The caller is responsible for ensuring that the resulting scalar is
293    /// used safely with respect to the target curve's cofactor and subgroup
294    /// properties.
295    pub fn from_seed(seed: &[u8]) -> Self {
296        let mut cnt = 0_u8;
297        let scalar = loop {
298            let mut hasher = S::Hasher::new();
299            hasher.update(seed);
300            if cnt > 0 {
301                hasher.update([cnt]);
302            }
303            let bytes = hasher.finalize();
304            let scalar = ScalarField::<S>::from_le_bytes_mod_order(&bytes[..]);
305            if !scalar.is_zero() {
306                break scalar;
307            }
308            cnt += 1;
309        };
310        Self::from_scalar(scalar)
311    }
312
313    /// Construct an ephemeral `Secret` using the provided randomness source.
314    pub fn from_rand(rng: &mut impl ark_std::rand::RngCore) -> Self {
315        let mut seed = [0u8; 32];
316        rng.fill_bytes(&mut seed);
317        Self::from_seed(&seed)
318    }
319
320    /// Get the associated public key.
321    pub fn public(&self) -> Public<S> {
322        self.public
323    }
324
325    /// Get the VRF output point relative to input.
326    pub fn output(&self, input: Input<S>) -> Output<S> {
327        Output(smul!(input.0, self.scalar).into_affine())
328    }
329}
330
331/// Public key generic over the cipher suite.
332///
333/// Elliptic curve point representing the public component of a VRF key pair.
334#[derive(Debug, Copy, Clone, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
335pub struct Public<S: Suite>(pub AffinePoint<S>);
336
337impl<S: Suite> Public<S> {
338    /// Construct from inner affine point.
339    ///
340    /// This allows creating a public key from an existing curve point.
341    pub fn from(value: AffinePoint<S>) -> Self {
342        Self(value)
343    }
344}
345
346/// VRF input point generic over the cipher suite.
347///
348/// Elliptic curve point representing the VRF input.
349#[derive(Debug, Clone, Copy, PartialEq, Eq, CanonicalSerialize, CanonicalDeserialize)]
350pub struct Input<S: Suite>(pub AffinePoint<S>);
351
352impl<S: Suite> Input<S> {
353    /// Construct from [`Suite::data_to_point`].
354    ///
355    /// Maps arbitrary data to a curve point via hash-to-curve.
356    pub fn new(data: &[u8]) -> Option<Self> {
357        S::data_to_point(data).map(Input)
358    }
359
360    /// Construct from inner affine point.
361    pub fn from(value: AffinePoint<S>) -> Self {
362        Self(value)
363    }
364}
365
366/// VRF output point generic over the cipher suite.
367///
368/// Elliptic curve point representing the VRF output.
369#[derive(Debug, Clone, Copy, PartialEq, Eq, CanonicalSerialize, CanonicalDeserialize)]
370pub struct Output<S: Suite>(pub AffinePoint<S>);
371
372impl<S: Suite> Output<S> {
373    /// Construct from inner affine point.
374    ///
375    /// This allows creating an output from an existing curve point.
376    pub fn from(value: AffinePoint<S>) -> Self {
377        Self(value)
378    }
379
380    /// Hash the output point to a deterministic byte string.
381    pub fn hash(&self) -> HashOutput<S> {
382        S::point_to_hash(&self.0)
383    }
384}
385
386/// Type aliases for the given suite.
387#[macro_export]
388macro_rules! suite_types {
389    ($suite:ident) => {
390        #[allow(dead_code)]
391        pub type Secret = $crate::Secret<$suite>;
392        #[allow(dead_code)]
393        pub type Public = $crate::Public<$suite>;
394        #[allow(dead_code)]
395        pub type Input = $crate::Input<$suite>;
396        #[allow(dead_code)]
397        pub type Output = $crate::Output<$suite>;
398        #[allow(dead_code)]
399        pub type AffinePoint = $crate::AffinePoint<$suite>;
400        #[allow(dead_code)]
401        pub type ScalarField = $crate::ScalarField<$suite>;
402        #[allow(dead_code)]
403        pub type BaseField = $crate::BaseField<$suite>;
404        #[allow(dead_code)]
405        pub type IetfProof = $crate::ietf::Proof<$suite>;
406        #[allow(dead_code)]
407        pub type PedersenProof = $crate::pedersen::Proof<$suite>;
408    };
409}
410
411#[cfg(test)]
412mod tests {
413    use super::*;
414    use suites::testing::{Input, Secret};
415    use testing::{TEST_SEED, random_val};
416
417    #[test]
418    fn vrf_output_check() {
419        use ark_std::rand::SeedableRng;
420        let mut rng = rand_chacha::ChaCha20Rng::from_seed([42; 32]);
421        let secret = Secret::from_seed(TEST_SEED);
422        let input = Input::from(random_val(Some(&mut rng)));
423        let output = secret.output(input);
424
425        let expected = "71c1b2ee6e46c59e3bd0e2f0e2852b90ab56abb223180b00bd6c8ec6b11af18c";
426        assert_eq!(expected, hex::encode(output.hash()));
427    }
428}