ark_vrf/
lib.rs

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