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}