Skip to main content

ark_vrf/
tiny.rs

1//! # Tiny VRF
2//!
3//! Compact VRF-AD scheme producing a short `(c, s)` proof. Prepends the Schnorr
4//! pair `(G, Y)` to the I/O list and proves a single DLEQ on the delinearized
5//! merged pair. The challenge scalar `c` is stored instead of the nonce commitment,
6//! yielding a smaller proof at the cost of not supporting batch verification.
7//!
8//! ## Usage
9//!
10//! ```rust,ignore
11//! use ark_vrf::suites::bandersnatch::*;
12//! use ark_vrf::tiny::{Prover, Verifier};
13//!
14//! let secret = Secret::from_seed([0; 32]);
15//! let public = secret.public();
16//! let input = Input::new(b"example input").unwrap();
17//! let io = secret.vrf_io(input);
18//!
19//! // Proving
20//! let proof = secret.prove(io, b"aux data");
21//!
22//! // Verification
23//! let result = public.verify(io, b"aux data", &proof);
24//! ```
25
26use super::*;
27use utils::common::DomSep;
28use utils::straus::short_msm;
29
30/// Marker trait for suites that support the Tiny VRF scheme.
31///
32/// Blanket-implemented for all types implementing [`Suite`].
33pub trait TinySuite: Suite {}
34
35impl<T> TinySuite for T where T: Suite {}
36
37#[inline(always)]
38fn vrf_transcript<S: TinySuite>(
39    public: AffinePoint<S>,
40    ios: impl AsRef<[VrfIo<S>]>,
41    ad: impl AsRef<[u8]>,
42) -> (S::Transcript, VrfIo<S>) {
43    utils::vrf_transcript_with_schnorr(DomSep::TinyVrf, public, ios, ad)
44}
45
46/// Tiny VRF proof.
47///
48/// Schnorr-based proof of correctness for a VRF evaluation:
49/// - `c`: Challenge scalar derived from public parameters
50/// - `s`: Response scalar satisfying the verification equation
51#[derive(Debug, Clone)]
52pub struct Proof<S: TinySuite> {
53    /// Challenge scalar derived from public parameters.
54    pub c: ScalarField<S>,
55    /// Response scalar satisfying the verification equation.
56    pub s: ScalarField<S>,
57}
58
59impl<S: TinySuite> CanonicalSerialize for Proof<S> {
60    fn serialize_with_mode<W: ark_serialize::Write>(
61        &self,
62        mut writer: W,
63        compress: ark_serialize::Compress,
64    ) -> Result<(), ark_serialize::SerializationError> {
65        let scalar_len = ScalarField::<S>::MODULUS_BIT_SIZE.div_ceil(8) as usize;
66        if scalar_len < utils::common::CHALLENGE_LEN {
67            // Encoded scalar length must be at least utils::common::CHALLENGE_LEN
68            return Err(ark_serialize::SerializationError::InvalidData);
69        }
70        let mut c_buf = [0; 128];
71        self.c
72            .serialize_compressed(&mut c_buf[..])
73            .expect("c_buf is big enough");
74        let c_buf = &c_buf[..utils::common::CHALLENGE_LEN];
75        writer.write_all(c_buf)?;
76        self.s.serialize_with_mode(&mut writer, compress)?;
77        Ok(())
78    }
79
80    fn serialized_size(&self, compress: ark_serialize::Compress) -> usize {
81        utils::common::CHALLENGE_LEN + self.s.serialized_size(compress)
82    }
83}
84
85impl<S: TinySuite> CanonicalDeserialize for Proof<S> {
86    fn deserialize_with_mode<R: ark_serialize::Read>(
87        mut reader: R,
88        compress: ark_serialize::Compress,
89        validate: ark_serialize::Validate,
90    ) -> Result<Self, ark_serialize::SerializationError> {
91        let mut c_buf = [0u8; utils::common::CHALLENGE_LEN];
92        if reader.read_exact(&mut c_buf[..]).is_err() {
93            return Err(ark_serialize::SerializationError::InvalidData);
94        }
95        let c = ScalarField::<S>::from_le_bytes_mod_order(&c_buf);
96        let s = <ScalarField<S> as CanonicalDeserialize>::deserialize_with_mode(
97            &mut reader,
98            compress,
99            validate,
100        )?;
101        Ok(Proof { c, s })
102    }
103}
104
105impl<S: TinySuite> ark_serialize::Valid for Proof<S> {
106    fn check(&self) -> Result<(), ark_serialize::SerializationError> {
107        self.c.check()?;
108        self.s.check()?;
109        Ok(())
110    }
111}
112
113/// Trait for types that can generate Tiny VRF proofs.
114pub trait Prover<S: TinySuite> {
115    /// Generate a proof for the given VRF I/O pairs and additional data.
116    ///
117    /// Multiple I/O pairs are delinearized into a single merged pair before proving.
118    fn prove(&self, ios: impl AsRef<[VrfIo<S>]>, ad: impl AsRef<[u8]>) -> Proof<S>;
119}
120
121/// Trait for entities that can verify Tiny VRF proofs.
122///
123/// All curve points involved in verification (public key and I/O pairs)
124/// are assumed to be in the prime-order subgroup. This is guaranteed
125/// when points are constructed through checked constructors ([`Public::from_affine`],
126/// [`Input::from_affine`], [`Output::from_affine`]) or through trusted
127/// operations like [`Input::new`] (hash-to-curve) and [`Secret::vrf_io`].
128///
129/// Using unchecked constructors (e.g. [`Input::from_affine_unchecked`]) places
130/// the burden of subgroup validation on the caller. Passing points with
131/// cofactor components leads to undefined verification behavior.
132pub trait Verifier<S: TinySuite> {
133    /// Verify a proof for the given VRF I/O pairs and additional data.
134    ///
135    /// Multiple I/O pairs are delinearized into a single merged pair before verifying.
136    ///
137    /// Returns `Ok(())` if verification succeeds, `Err(Error::VerificationFailure)` otherwise.
138    fn verify(
139        &self,
140        ios: impl AsRef<[VrfIo<S>]>,
141        aux: impl AsRef<[u8]>,
142        proof: &Proof<S>,
143    ) -> Result<(), Error>;
144}
145
146impl<S: TinySuite> Prover<S> for Secret<S> {
147    /// Tiny VRF proving algorithm.
148    ///
149    /// Prepends the Schnorr pair (G, Y) to the I/O list and proves a single
150    /// DLEQ on the delinearized merged pair:
151    ///
152    /// 1. Generate a deterministic nonce `k`
153    /// 2. Compute nonce commitment `R = k * I_m`
154    /// 3. Compute the challenge `c`
155    /// 4. Compute the response `s = k + c * x`
156    fn prove(&self, ios: impl AsRef<[VrfIo<S>]>, ad: impl AsRef<[u8]>) -> Proof<S> {
157        let (t, io) = vrf_transcript::<S>(self.public.0, ios, ad);
158
159        let k = S::nonce(&self.scalar, Some(t.clone()));
160
161        // R = k * I_m
162        let r = smul!(io.input.0, k).into_affine();
163
164        let c = S::challenge(&[&r], Some(t));
165        let s = k + c * self.scalar;
166        Proof { c, s }
167    }
168}
169
170impl<S: TinySuite> Verifier<S> for Public<S> {
171    /// Tiny VRF verification algorithm.
172    ///
173    /// 1. Compute `R = s * I_m - c * O_m`
174    /// 2. Recompute the expected challenge `c_exp`
175    /// 3. Verify that `c_exp == c`
176    fn verify(
177        &self,
178        ios: impl AsRef<[VrfIo<S>]>,
179        ad: impl AsRef<[u8]>,
180        proof: &Proof<S>,
181    ) -> Result<(), Error> {
182        let (t, io) = vrf_transcript::<S>(self.0, ios, ad);
183
184        let Proof { c, s } = proof;
185
186        // R = s * I_m - c * O_m
187        let r = short_msm(&[io.input.0, io.output.0], &[*s, -*c], 2).into_affine();
188
189        let c_exp = S::challenge(&[&r], Some(t));
190        (c_exp == *c)
191            .then_some(())
192            .ok_or(Error::VerificationFailure)
193    }
194}
195
196#[cfg(test)]
197pub mod testing {
198    use super::*;
199    use crate::testing::{self as common, SuiteExt};
200
201    pub fn prove_verify<S: TinySuite>() {
202        let secret = Secret::<S>::from_seed(common::TEST_SEED);
203        let public = secret.public();
204        let input = Input::from_affine_unchecked(common::random_val(None));
205        let io = secret.vrf_io(input);
206
207        let proof = secret.prove(io, b"foo");
208        let result = public.verify(io, b"foo", &proof);
209        assert!(result.is_ok());
210    }
211
212    pub fn prove_verify_multi_empty<S: TinySuite>() {
213        let secret = Secret::<S>::from_seed(common::TEST_SEED);
214        let public = secret.public();
215
216        let ios: [VrfIo<S>; 0] = [];
217        let proof = secret.prove(ios, b"bar");
218
219        assert!(public.verify(ios, b"bar", &proof).is_ok());
220
221        // Wrong ad should fail
222        assert!(public.verify(ios, b"baz", &proof).is_err());
223    }
224
225    /// N=1 slice produces same proof as passing a single `VrfIo`.
226    pub fn prove_verify_multi_single<S: TinySuite>() {
227        let secret = Secret::<S>::from_seed(common::TEST_SEED);
228        let public = secret.public();
229        let input = Input::from_affine_unchecked(common::random_val(None));
230        let io = secret.vrf_io(input);
231
232        let proof_single = secret.prove(io, b"foo");
233        let proof_slice = secret.prove([io], b"foo");
234
235        // Byte-identical proofs
236        let encode = |p: &tiny::Proof<S>| {
237            let mut buf = Vec::new();
238            p.serialize_compressed(&mut buf).unwrap();
239            buf
240        };
241        assert_eq!(encode(&proof_single), encode(&proof_slice));
242
243        // Cross-verification
244        assert!(public.verify(io, b"foo", &proof_slice).is_ok());
245        assert!(public.verify([io], b"foo", &proof_single).is_ok());
246    }
247
248    /// N=3 multi proof: verify succeeds; tampered output/input/ad fails.
249    pub fn prove_verify_multi<S: TinySuite>() {
250        let secret = Secret::<S>::from_seed(common::TEST_SEED);
251        let public = secret.public();
252
253        let mut ios: Vec<VrfIo<S>> = (0..3u8)
254            .map(|i| {
255                let input = Input::new(&[i + 1]).unwrap();
256                secret.vrf_io(input)
257            })
258            .collect();
259        ios.push(VrfIo {
260            input: Input(S::Affine::generator()),
261            output: Output(public.0),
262        });
263
264        let proof = secret.prove(&ios[..], b"bar");
265        assert!(public.verify(&ios[..], b"bar", &proof).is_ok());
266
267        // Tamper: wrong output on ios[1]
268        let mut bad_ios = ios.clone();
269        bad_ios[1].output = secret.output(ios[0].input);
270        assert!(public.verify(&bad_ios[..], b"bar", &proof).is_err());
271
272        // Tamper: wrong input on ios[0]
273        let mut bad_ios = ios.clone();
274        bad_ios[0].input = ios[1].input;
275        assert!(public.verify(&bad_ios[..], b"bar", &proof).is_err());
276
277        // Tamper: wrong ad
278        assert!(public.verify(&ios[..], b"baz", &proof).is_err());
279    }
280
281    #[macro_export]
282    macro_rules! tiny_suite_tests {
283        ($suite:ty) => {
284            mod tiny {
285                use super::*;
286
287                #[test]
288                fn prove_verify() {
289                    $crate::tiny::testing::prove_verify::<$suite>();
290                }
291
292                #[test]
293                fn prove_verify_multi_single() {
294                    $crate::tiny::testing::prove_verify_multi_single::<$suite>();
295                }
296
297                #[test]
298                fn prove_verify_multi() {
299                    $crate::tiny::testing::prove_verify_multi::<$suite>();
300                }
301
302                #[test]
303                fn prove_verify_multi_empty() {
304                    $crate::tiny::testing::prove_verify_multi_empty::<$suite>();
305                }
306
307                $crate::test_vectors!($crate::tiny::testing::TestVector<$suite>);
308            }
309        };
310    }
311
312    pub struct TestVector<S: TinySuite> {
313        pub base: common::TestVector<S>,
314        pub c: ScalarField<S>,
315        pub s: ScalarField<S>,
316    }
317
318    impl<S: TinySuite> core::fmt::Debug for TestVector<S> {
319        fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
320            let c = hex::encode(common::scalar_encode::<S>(&self.c));
321            let s = hex::encode(common::scalar_encode::<S>(&self.s));
322            f.debug_struct("TestVector")
323                .field("base", &self.base)
324                .field("proof_c", &c)
325                .field("proof_s", &s)
326                .finish()
327        }
328    }
329
330    impl<S> common::TestVectorTrait for TestVector<S>
331    where
332        S: TinySuite + SuiteExt + std::fmt::Debug,
333    {
334        fn name() -> String {
335            S::SUITE_NAME.to_string() + "_tiny"
336        }
337
338        fn new(comment: &str, seed: &[u8; 32], alpha: &[u8], ad: &[u8]) -> Self {
339            use super::Prover;
340            let base = common::TestVector::new(comment, seed, alpha, ad);
341            let io = VrfIo {
342                input: Input::from_affine_unchecked(base.h),
343                output: Output::from_affine_unchecked(base.gamma),
344            };
345            let sk = Secret::from_scalar(base.sk);
346            let proof: Proof<S> = sk.prove(io, ad);
347            Self {
348                base,
349                c: proof.c,
350                s: proof.s,
351            }
352        }
353
354        fn from_map(map: &common::TestVectorMap) -> Self {
355            let base = common::TestVector::from_map(map);
356            let c = common::scalar_decode::<S>(&map.get_bytes("proof_c"));
357            let s = common::scalar_decode::<S>(&map.get_bytes("proof_s"));
358            Self { base, c, s }
359        }
360
361        fn to_map(&self) -> common::TestVectorMap {
362            let buf = common::scalar_encode::<S>(&self.c);
363            let proof_c = &buf[..utils::common::CHALLENGE_LEN];
364            let items = [
365                ("proof_c", hex::encode(proof_c)),
366                ("proof_s", hex::encode(common::scalar_encode::<S>(&self.s))),
367            ];
368            let mut map = self.base.to_map();
369            items.into_iter().for_each(|(name, value)| {
370                map.0.insert(name.to_string(), value);
371            });
372            map
373        }
374
375        fn run(&self) {
376            self.base.run();
377            let io = VrfIo {
378                input: Input::<S>::from_affine_unchecked(self.base.h),
379                output: Output::from_affine_unchecked(self.base.gamma),
380            };
381            let sk = Secret::from_scalar(self.base.sk);
382            let proof = sk.prove(io, &self.base.ad);
383            assert_eq!(self.c, proof.c, "VRF proof challenge ('c') mismatch");
384            assert_eq!(self.s, proof.s, "VRF proof response ('s') mismatch");
385
386            let pk = Public(self.base.pk);
387            assert!(pk.verify(io, &self.base.ad, &proof).is_ok());
388        }
389    }
390}