Skip to main content

ark_vrf/
pedersen.rs

1//! # Pedersen VRF
2//!
3//! Key-hiding VRF based on the PedVRF construction from Section 4 of
4//! [BCHSV23](https://eprint.iacr.org/2023/002). Replaces the public key with a
5//! Pedersen commitment to the secret key, allowing verification without revealing
6//! which specific public key was used. Serves as a building block for anonymized
7//! ring signatures.
8//!
9//! ## Usage
10//!
11//! ```rust,ignore
12//! use ark_vrf::suites::bandersnatch::*;
13//! use ark_vrf::pedersen::{Prover, Verifier};
14//!
15//! let secret = Secret::from_seed([0; 32]);
16//! let public = secret.public();
17//! let input = Input::new(b"example input").unwrap();
18//! let io = secret.vrf_io(input);
19//!
20//! // Proving
21//! let (proof, blinding) = secret.prove(io, b"aux data");
22//!
23//! // Verification
24//! let result = Public::verify(io, b"aux data", &proof);
25//!
26//! // Unblinding: verify the proof was created using a specific public key
27//! let expected = (public.0 + BandersnatchSha512Ell2::BLINDING_BASE * blinding).into_affine();
28//! assert_eq!(proof.key_commitment(), expected);
29//! ```
30
31use crate::Suite;
32use crate::utils;
33use crate::utils::common::DomSep;
34use crate::utils::straus::short_msm;
35use crate::*;
36use ark_ec::VariableBaseMSM;
37
38/// Seed hashed to curve to produce [`PedersenSuite::BLINDING_BASE`] in built-in suites.
39pub const PEDERSEN_BLINDING_BASE_SEED: &[u8] = b"pedersen-blinding";
40
41/// Suite extension for Pedersen VRF support.
42///
43/// Provides the additional cryptographic parameters required by the Pedersen VRF scheme.
44pub trait PedersenSuite: Suite {
45    /// Blinding base.
46    const BLINDING_BASE: AffinePoint<Self>;
47
48    /// Pedersen blinding factor.
49    ///
50    /// Default implementation is deterministic. All parameters but `secret` are public.
51    fn blinding(secret: &ScalarField<Self>, mut transcript: Self::Transcript) -> ScalarField<Self> {
52        transcript.absorb_raw(&[DomSep::PedersenBlinding as u8]);
53        Self::nonce(secret, Some(transcript))
54    }
55}
56
57/// Pedersen VRF proof.
58///
59/// Zero-knowledge proof with key-hiding properties:
60/// - `pk_com`: Commitment to the public key (Y_b = x·G + b·B)
61/// - `r`: Nonce commitment for the generator (R = k·G + k_b·B)
62/// - `ok`: Nonce commitment for the input point (O_k = k·I)
63/// - `s`: Response scalar for the secret key
64/// - `sb`: Response scalar for the blinding factor
65///
66/// Deserialization via [`CanonicalDeserialize`] includes subgroup checks for
67/// curve points, so deserialized proofs are guaranteed to contain valid points.
68#[derive(Debug, Clone, CanonicalSerialize, CanonicalDeserialize)]
69pub struct Proof<S: PedersenSuite> {
70    pk_com: AffinePoint<S>,
71    r: AffinePoint<S>,
72    ok: AffinePoint<S>,
73    s: ScalarField<S>,
74    sb: ScalarField<S>,
75}
76
77impl<S: PedersenSuite> Proof<S> {
78    /// Get public key commitment from proof.
79    pub fn key_commitment(&self) -> AffinePoint<S> {
80        self.pk_com
81    }
82}
83
84/// Trait for types that can generate Pedersen VRF proofs.
85pub trait Prover<S: PedersenSuite> {
86    /// Generate a proof for the given VRF I/O pairs and additional data.
87    ///
88    /// Multiple I/O pairs are delinearized into a single merged pair before proving.
89    ///
90    /// Returns the proof together with the associated blinding factor.
91    fn prove(
92        &self,
93        ios: impl AsRef<[VrfIo<S>]>,
94        ad: impl AsRef<[u8]>,
95    ) -> (Proof<S>, ScalarField<S>);
96}
97
98/// Trait for entities that can verify Pedersen VRF proofs.
99///
100/// Verifies that a VRF output is correctly derived from an input using a
101/// committed public key, without revealing which specific public key was used.
102///
103/// All curve points involved in verification (I/O pairs and proof points)
104/// are assumed to be in the prime-order subgroup. This is guaranteed when
105/// points are constructed through checked constructors ([`Input::from_affine`],
106/// [`Output::from_affine`]) or through trusted operations like [`Input::new`]
107/// (hash-to-curve) and [`Secret::vrf_io`]. Proof points are guaranteed valid
108/// when deserialized via [`CanonicalDeserialize`] (which includes subgroup
109/// checks) or produced by [`Prover::prove`].
110///
111/// Using unchecked constructors (e.g. [`Input::from_affine_unchecked`]) places
112/// the burden of subgroup validation on the caller. Passing points with
113/// cofactor components leads to undefined verification behavior.
114///
115/// The group identity is checked unconditionally, for the key commitment and
116/// for every I/O pair. Neither binds the proof to a signer: the opening of the
117/// identity commitment is the public `(0, 0)`, and a pair holding the identity
118/// is satisfied by every secret key. It stays a legal value for the nonce
119/// commitments `R` and `Ok`, which commit to nothing, and `Ok` is necessarily
120/// the identity when no I/O pair is supplied.
121pub trait Verifier<S: PedersenSuite> {
122    /// Verify a proof for the given VRF I/O pairs and additional data.
123    ///
124    /// Multiple I/O pairs are delinearized into a single merged pair before verifying.
125    ///
126    /// Returns `Ok(())` if verification succeeds, `Err(Error::InvalidData)` if the
127    /// key commitment or any I/O pair point is the group identity,
128    /// `Err(Error::VerificationFailure)` otherwise.
129    fn verify(
130        ios: impl AsRef<[VrfIo<S>]>,
131        ad: impl AsRef<[u8]>,
132        proof: &Proof<S>,
133    ) -> Result<(), Error>;
134}
135
136impl<S: PedersenSuite> Prover<S> for Secret<S> {
137    fn prove(
138        &self,
139        ios: impl AsRef<[VrfIo<S>]>,
140        ad: impl AsRef<[u8]>,
141    ) -> (Proof<S>, ScalarField<S>) {
142        let (mut t, io) = utils::vrf_transcript::<S>(DomSep::PedersenVrf, ios, ad);
143
144        // Build blinding factor from T.fork()
145        let blinding = S::blinding(&self.scalar, t.clone());
146
147        // Yb = x*G + b*B = PK + b*B
148        let bb = smul!(S::BLINDING_BASE, blinding);
149        let pk_com = (self.public.0.into_group() + bb).into_affine();
150
151        // Absorb Yb into the transcript
152        t.absorb_serialize(&pk_com);
153
154        // Nonces from T.fork()
155        let k = S::nonce(&self.scalar, Some(t.clone()));
156        let kb = S::nonce(&blinding, Some(t.clone()));
157
158        // R = k*G + kb*B
159        let kg = smul!(S::generator(), k);
160        let kbb = smul!(S::BLINDING_BASE, kb);
161        let r = kg + kbb;
162
163        // Ok = k*I
164        let ok = smul!(io.input.0, k);
165
166        let norms = CurveGroup::normalize_batch(&[r, ok]);
167        let (r, ok) = (norms[0], norms[1]);
168
169        // c = challenge([R, Ok], T)
170        let c = S::challenge(&[&r, &ok], Some(t));
171
172        // s = k + c*x
173        let s = k + c * self.scalar;
174        // sb = kb + c*b
175        let sb = kb + c * blinding;
176
177        let proof = Proof {
178            pk_com,
179            r,
180            ok,
181            s,
182            sb,
183        };
184        (proof, blinding)
185    }
186}
187
188impl<S: PedersenSuite> Verifier<S> for Public<S> {
189    fn verify(
190        ios: impl AsRef<[VrfIo<S>]>,
191        ad: impl AsRef<[u8]>,
192        proof: &Proof<S>,
193    ) -> Result<(), Error> {
194        let Proof {
195            pk_com,
196            r,
197            ok,
198            s,
199            sb,
200        } = proof;
201
202        // Yb = 0 is the commitment of the opening (0, 0), which is public, so
203        // anyone can satisfy Eq2 without knowing a secret.
204        if pk_com.is_zero() {
205            return Err(Error::InvalidData);
206        }
207
208        // A pair holding the identity satisfies O = x*I for every x, so it
209        // binds its VRF output to no signer.
210        let ios = ios.as_ref();
211        if ios.iter().any(VrfIo::has_identity) {
212            return Err(Error::InvalidData);
213        }
214
215        let (mut t, io) = utils::vrf_transcript::<S>(DomSep::PedersenVrf, ios, ad);
216
217        // Absorb Yb into the transcript
218        t.absorb_serialize(pk_com);
219
220        // c = challenge([R, Ok], T)
221        let c = S::challenge(&[r, ok], Some(t));
222
223        let neg_c = -c;
224
225        // Eq1: s*I - c*O == Ok
226        // Verifies that the VRF output O is correctly derived from the input I
227        // using the same secret scalar x committed in the proof. Expanding the
228        // response s = k + c*x gives s*I = k*I + c*x*I = Ok + c*O.
229        let lhs1 = short_msm(&[io.input.0, io.output.0], &[*s, neg_c], 2);
230        if lhs1 != ok.into_group() {
231            return Err(Error::VerificationFailure);
232        }
233
234        // Eq2: s*G + sb*B - c*Yb == R
235        // Verifies knowledge of both the secret key x and blinding factor b
236        // committed in the public key commitment Yb = x*G + b*B. Expanding
237        // s = k + c*x and sb = kb + c*b gives s*G + sb*B = R + c*Yb.
238        let lhs2 = short_msm(
239            &[S::generator(), S::BLINDING_BASE, *pk_com],
240            &[*s, *sb, neg_c],
241            2,
242        );
243        if lhs2 != r.into_group() {
244            return Err(Error::VerificationFailure);
245        }
246
247        Ok(())
248    }
249}
250
251/// Deferred Pedersen verification data for batch verification.
252///
253/// Captures all the information needed to verify a single Pedersen proof,
254/// allowing multiple proofs to be verified together via a single MSM.
255pub struct BatchItem<S: PedersenSuite> {
256    c: ScalarField<S>,
257    input: AffinePoint<S>,
258    output: AffinePoint<S>,
259    pk_com: AffinePoint<S>,
260    r: AffinePoint<S>,
261    ok: AffinePoint<S>,
262    s: ScalarField<S>,
263    sb: ScalarField<S>,
264    /// Set when a supplied I/O pair held the identity. Recorded here because
265    /// only the merged pair survives, and that one is legitimately the identity
266    /// when no pair is supplied at all.
267    io_identity: bool,
268}
269
270impl<S: PedersenSuite> BatchItem<S> {
271    /// Prepare a proof for batch verification.
272    ///
273    /// Computes the challenge and packages all data needed for deferred
274    /// verification. This is cheap (one hash, no scalar multiplications)
275    /// and can be done in parallel.
276    pub fn new(ios: impl AsRef<[VrfIo<S>]>, ad: impl AsRef<[u8]>, proof: &Proof<S>) -> Self {
277        let ios = ios.as_ref();
278        let io_identity = ios.iter().any(VrfIo::has_identity);
279        let (mut t, io) = utils::vrf_transcript::<S>(DomSep::PedersenVrf, ios, ad);
280        t.absorb_serialize(&proof.pk_com);
281        let c = S::challenge(&[&proof.r, &proof.ok], Some(t));
282        Self {
283            c,
284            input: io.input.0,
285            output: io.output.0,
286            pk_com: proof.pk_com,
287            r: proof.r,
288            ok: proof.ok,
289            s: proof.s,
290            sb: proof.sb,
291            io_identity,
292        }
293    }
294}
295
296/// Batch verifier for Pedersen VRF proofs.
297///
298/// Collects multiple proofs and verifies them together via a single
299/// multi-scalar multiplication.
300///
301/// The same subgroup membership assumptions as [`Verifier`] apply to all
302/// points fed into the batch (I/O pairs and proof points).
303pub struct BatchVerifier<S: PedersenSuite> {
304    items: Vec<BatchItem<S>>,
305}
306
307impl<S: PedersenSuite> Default for BatchVerifier<S> {
308    fn default() -> Self {
309        Self { items: Vec::new() }
310    }
311}
312
313impl<S: PedersenSuite> BatchVerifier<S> {
314    /// Create a new empty batch verifier.
315    pub fn new() -> Self {
316        Self::default()
317    }
318
319    /// Push a previously prepared entry into the batch.
320    pub fn push_prepared(&mut self, entry: BatchItem<S>) {
321        self.items.push(entry);
322    }
323
324    /// Prepare and push a proof in one step.
325    pub fn push(&mut self, ios: impl AsRef<[VrfIo<S>]>, ad: impl AsRef<[u8]>, proof: &Proof<S>) {
326        self.push_prepared(BatchItem::new(ios, ad, proof));
327    }
328
329    /// Batch-verify multiple Pedersen proofs using a single multi-scalar multiplication.
330    ///
331    /// For each proof i, two equations are checked with independent random scalars
332    /// t_i (eq1) and u_i (eq2):
333    ///   Eq1: O_i*c_i + Ok_i == I_i*s_i
334    ///   Eq2: Yb_i*c_i + R_i == G*s_i + B*sb_i
335    ///
336    /// The random linear combination yields a (5N + 2)-point MSM.
337    ///
338    /// Returns `Ok(())` if all proofs verify, `Err(Error::InvalidData)` if any
339    /// key commitment or I/O pair point is the group identity,
340    /// `Err(VerificationFailure)` otherwise.
341    pub fn verify(&self) -> Result<(), Error> {
342        let items = &self.items;
343        if items.is_empty() {
344            return Ok(());
345        }
346
347        // Checked here rather than in `BatchItem::new`, which cannot fail.
348        if items
349            .iter()
350            .any(|item| item.pk_com.is_zero() || item.io_identity)
351        {
352            return Err(Error::InvalidData);
353        }
354
355        let n = items.len();
356
357        // Generate deterministic random scalars from entry data.
358        // Absorb (c, s, sb) per entry, then squeeze 2N random scalars.
359        // The challenge c already commits to (Yb, I, O, R, Ok, ad), so only the
360        // response scalars s and sb need to be included separately.
361        let mut t = S::Transcript::new(S::SUITE_ID);
362        t.absorb_raw(&[DomSep::BatchVerify as u8]);
363        for e in items {
364            t.absorb_serialize(&e.c);
365            t.absorb_serialize(&e.s);
366            t.absorb_serialize(&e.sb);
367        }
368        // Sample 2N random 128-bit scalars (t_i for eq1, u_i for eq2).
369        // 128-bit scalars are sufficient for the Schwartz-Zippel soundness argument
370        // (error probability 2^{-128}) and roughly halve the MSM cost compared to
371        // full-width field elements, since fewer doublings are needed in the
372        // Pippenger/Straus window.
373        let random_scalars: Vec<(ScalarField<S>, ScalarField<S>)> = (0..n)
374            .map(|_| {
375                let mut buf = [0u8; 32];
376                t.squeeze_raw(&mut buf);
377                let t = ScalarField::<S>::from_le_bytes_mod_order(&buf[..16]);
378                let u = ScalarField::<S>::from_le_bytes_mod_order(&buf[16..]);
379                (t, u)
380            })
381            .collect();
382
383        // Build MSM: 5N per-proof points + 2 shared bases (G, B)
384        let mut bases = Vec::with_capacity(5 * n + 2);
385        let mut scalars = Vec::with_capacity(5 * n + 2);
386
387        let mut g_scalar = ScalarField::<S>::zero();
388        let mut b_scalar = ScalarField::<S>::zero();
389
390        for (e, (t, u)) in items.iter().zip(random_scalars.iter()) {
391            // Eq1: t_i*c_i*O_i + t_i*Ok_i - t_i*s_i*I_i = 0
392            bases.push(e.output);
393            scalars.push(*t * e.c);
394
395            bases.push(e.ok);
396            scalars.push(*t);
397
398            bases.push(e.input);
399            scalars.push(-(*t * e.s));
400
401            // Eq2: u_i*c_i*Yb_i + u_i*R_i - u_i*s_i*G - u_i*sb_i*B = 0
402            bases.push(e.pk_com);
403            scalars.push(*u * e.c);
404
405            bases.push(e.r);
406            scalars.push(*u);
407
408            // Accumulate shared base scalars
409            g_scalar += *u * e.s;
410            b_scalar += *u * e.sb;
411        }
412
413        // Shared bases: G and B
414        bases.push(S::generator());
415        scalars.push(-g_scalar);
416
417        bases.push(S::BLINDING_BASE);
418        scalars.push(-b_scalar);
419
420        let result = <S::Affine as AffineRepr>::Group::msm_unchecked(&bases, &scalars);
421        if !result.is_zero() {
422            return Err(Error::VerificationFailure);
423        }
424
425        Ok(())
426    }
427}
428
429#[cfg(test)]
430pub(crate) mod testing {
431    use super::*;
432    use crate::testing::{self as common, CheckPoint, SuiteExt, TEST_SEED, random_val};
433
434    pub fn prove_verify<S: PedersenSuite>() {
435        use pedersen::{Prover, Verifier};
436
437        let secret = Secret::<S>::from_seed(TEST_SEED);
438        let input = Input::from_affine_unchecked(random_val(None));
439        let io = secret.vrf_io(input);
440
441        let (proof, blinding) = secret.prove(io, b"foo");
442        let result = Public::verify(io, b"foo", &proof);
443        assert!(result.is_ok());
444
445        assert_eq!(
446            proof.key_commitment(),
447            (secret.public().0 + S::BLINDING_BASE * blinding).into()
448        );
449    }
450
451    pub fn batch_verify<S: PedersenSuite>() {
452        use pedersen::{BatchItem, BatchVerifier, Prover, Verifier};
453
454        let secret = Secret::<S>::from_seed(TEST_SEED);
455        let input = Input::from_affine_unchecked(random_val(None));
456        let io = secret.vrf_io(input);
457
458        let (proof1, _) = secret.prove(io, b"foo");
459        let (proof2, _) = secret.prove(io, b"bar");
460
461        // Single-proof verification still works.
462        assert!(Public::verify(io, b"foo", &proof1).is_ok());
463        assert!(Public::verify(io, b"bar", &proof2).is_ok());
464
465        // Batch using push.
466        let mut batch = BatchVerifier::new();
467        batch.push(io, b"foo", &proof1);
468        batch.push(io, b"bar", &proof2);
469        assert!(batch.verify().is_ok());
470
471        // Batch using BatchItem::new + push_prepared.
472        let mut batch = BatchVerifier::new();
473        let entry1 = BatchItem::new(io, b"foo", &proof1);
474        let entry2 = BatchItem::new(io, b"bar", &proof2);
475        batch.push_prepared(entry1);
476        batch.push_prepared(entry2);
477        assert!(batch.verify().is_ok());
478
479        // Empty batch is ok.
480        let batch = BatchVerifier::<S>::new();
481        assert!(batch.verify().is_ok());
482
483        // Bad additional data should fail.
484        let mut batch = BatchVerifier::new();
485        batch.push(io, b"foo", &proof1);
486        batch.push(io, b"wrong", &proof2);
487        assert!(batch.verify().is_err());
488    }
489
490    /// N=1 slice produces same proof as passing a single `VrfIo`.
491    pub fn prove_verify_multi_single<S: PedersenSuite>() {
492        use pedersen::{Prover, Verifier};
493
494        let secret = Secret::<S>::from_seed(TEST_SEED);
495        let input = Input::from_affine_unchecked(random_val(None));
496        let io = secret.vrf_io(input);
497
498        let (proof_single, blinding_single) = secret.prove(io, b"foo");
499        let (proof_slice, blinding_slice) = secret.prove([io], b"foo");
500
501        // Byte-identical proofs and blinding factors
502        let encode = |p: &pedersen::Proof<S>| {
503            let mut buf = Vec::new();
504            p.serialize_compressed(&mut buf).unwrap();
505            buf
506        };
507        assert_eq!(encode(&proof_single), encode(&proof_slice));
508        assert_eq!(blinding_single, blinding_slice);
509
510        // Cross-verification
511        assert!(Public::verify(io, b"foo", &proof_slice).is_ok());
512        assert!(Public::verify([io], b"foo", &proof_single).is_ok());
513    }
514
515    /// N=3 multi proof: verify succeeds; tampered output/input/ad fails.
516    pub fn prove_verify_multi<S: PedersenSuite>() {
517        use pedersen::{Prover, Verifier};
518
519        let secret = Secret::<S>::from_seed(TEST_SEED);
520
521        let mut ios: Vec<VrfIo<S>> = (0..3u8)
522            .map(|i| {
523                let input = Input::new(&[i + 1]).unwrap();
524                secret.vrf_io(input)
525            })
526            .collect();
527        ios.push(VrfIo {
528            input: Input(S::Affine::generator()),
529            output: Output(secret.public().0),
530        });
531
532        let (proof, _) = secret.prove(&ios[..], b"bar");
533        assert!(Public::verify(&ios[..], b"bar", &proof).is_ok());
534
535        // Tamper: wrong output on ios[1]
536        let mut bad_ios = ios.clone();
537        bad_ios[1].output = secret.output(ios[0].input);
538        assert!(Public::verify(&bad_ios[..], b"bar", &proof).is_err());
539
540        // Tamper: wrong input on ios[0]
541        let mut bad_ios = ios.clone();
542        bad_ios[0].input = ios[1].input;
543        assert!(Public::verify(&bad_ios[..], b"bar", &proof).is_err());
544
545        // Tamper: wrong ad
546        assert!(Public::verify(&ios[..], b"baz", &proof).is_err());
547    }
548
549    /// N=0 reduces to a Schnorr signature over the additional data.
550    pub fn prove_verify_multi_empty<S: PedersenSuite>() {
551        use pedersen::{Prover, Verifier};
552
553        let secret = Secret::<S>::from_seed(TEST_SEED);
554
555        let ios: [VrfIo<S>; 0] = [];
556        let (proof, _) = secret.prove(ios, b"bar");
557
558        assert!(Public::verify(ios, b"bar", &proof).is_ok());
559
560        // Wrong ad should fail
561        assert!(Public::verify(ios, b"baz", &proof).is_err());
562    }
563
564    /// An I/O pair holding the identity must be rejected by both verifiers.
565    ///
566    /// `(I, O) = (0, 0)` satisfies `O = x * I` for every secret key, so both
567    /// verification equations accept it and two different keys produce two
568    /// valid proofs for the same pair. The pair therefore binds its VRF output
569    /// to nobody, and only an explicit check keeps it out. The second case
570    /// hides the bad pair behind a good one, where the merged pair alone is not
571    /// enough to catch it.
572    pub fn identity_io_pair_rejected<S: PedersenSuite>() {
573        use pedersen::{BatchVerifier, Prover, Verifier};
574
575        let identity_io = VrfIo::<S> {
576            input: Input(AffinePoint::<S>::zero()),
577            output: Output(AffinePoint::<S>::zero()),
578        };
579
580        for seed in [TEST_SEED, [0x11; 32]] {
581            let secret = Secret::<S>::from_seed(seed);
582
583            let (proof, _) = secret.prove([identity_io], b"forgery");
584            assert!(Public::verify([identity_io], b"forgery", &proof).is_err());
585
586            let mut batch = BatchVerifier::new();
587            batch.push([identity_io], b"forgery", &proof);
588            assert!(batch.verify().is_err());
589
590            let good_io = secret.vrf_io(Input::new(b"good").unwrap());
591            let ios = [good_io, identity_io];
592            let (proof, _) = secret.prove(ios, b"forgery");
593            assert!(Public::verify(ios, b"forgery", &proof).is_err());
594
595            let mut batch = BatchVerifier::new();
596            batch.push(ios, b"forgery", &proof);
597            assert!(batch.verify().is_err());
598        }
599    }
600
601    /// An identity key commitment must be rejected by both verifiers.
602    ///
603    /// `Yb = 0` is the commitment of the opening `(x, b) = (0, 0)`, which is
604    /// public. The proof below is built without any secret: both responses are
605    /// the nonces themselves, since the challenge multiplies zero. Both
606    /// verification equations hold, so only an explicit check keeps it out.
607    pub fn identity_key_commitment_rejected<S: PedersenSuite>() {
608        use pedersen::{BatchVerifier, Verifier};
609
610        let ios: [VrfIo<S>; 0] = [];
611        let ad = b"forgery";
612
613        let (mut t, io) = utils::vrf_transcript::<S>(DomSep::PedersenVrf, ios, ad);
614        let pk_com = AffinePoint::<S>::zero();
615        t.absorb_serialize(&pk_com);
616
617        let k = ScalarField::<S>::from(7u64);
618        let kb = ScalarField::<S>::from(11u64);
619        let r = (S::generator() * k + S::BLINDING_BASE * kb).into_affine();
620        let ok = (io.input.0 * k).into_affine();
621
622        let proof = Proof {
623            pk_com,
624            r,
625            ok,
626            s: k,
627            sb: kb,
628        };
629
630        assert!(Public::verify(ios, ad, &proof).is_err());
631
632        let mut batch = BatchVerifier::new();
633        batch.push(ios, ad, &proof);
634        assert!(batch.verify().is_err());
635    }
636
637    pub fn blinding_base_check<S: PedersenSuite>()
638    where
639        AffinePoint<S>: CheckPoint,
640    {
641        // Check that point has been computed using the magic spell.
642        assert_eq!(
643            S::BLINDING_BASE,
644            S::data_to_point(PEDERSEN_BLINDING_BASE_SEED).unwrap()
645        );
646        // Check that the point is on curve.
647        assert!(S::BLINDING_BASE.check(true).is_ok());
648    }
649
650    #[macro_export]
651    macro_rules! pedersen_suite_tests {
652        ($suite:ty) => {
653            mod pedersen {
654                use super::*;
655
656                #[test]
657                fn prove_verify() {
658                    $crate::pedersen::testing::prove_verify::<$suite>();
659                }
660
661                #[test]
662                fn prove_verify_multi_single() {
663                    $crate::pedersen::testing::prove_verify_multi_single::<$suite>();
664                }
665
666                #[test]
667                fn prove_verify_multi() {
668                    $crate::pedersen::testing::prove_verify_multi::<$suite>();
669                }
670
671                #[test]
672                fn prove_verify_multi_empty() {
673                    $crate::pedersen::testing::prove_verify_multi_empty::<$suite>();
674                }
675
676                #[test]
677                fn batch_verify() {
678                    $crate::pedersen::testing::batch_verify::<$suite>();
679                }
680
681                #[test]
682                fn identity_io_pair_rejected() {
683                    $crate::pedersen::testing::identity_io_pair_rejected::<$suite>();
684                }
685
686                #[test]
687                fn identity_key_commitment_rejected() {
688                    $crate::pedersen::testing::identity_key_commitment_rejected::<$suite>();
689                }
690
691                #[test]
692                fn blinding_base_check() {
693                    $crate::pedersen::testing::blinding_base_check::<$suite>();
694                }
695
696                $crate::test_vectors!($crate::pedersen::testing::TestVector<$suite>);
697            }
698        };
699    }
700
701    pub struct TestVector<S: PedersenSuite> {
702        pub base: common::TestVector<S>,
703        pub blind: ScalarField<S>,
704        pub proof: Proof<S>,
705    }
706
707    impl<S: PedersenSuite> core::fmt::Debug for TestVector<S> {
708        fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
709            f.debug_struct("TestVector")
710                .field("base", &self.base)
711                .field("blinding", &self.blind)
712                .field("proof_pk_com", &self.proof.pk_com)
713                .field("proof_r", &self.proof.r)
714                .field("proof_ok", &self.proof.ok)
715                .field("proof_s", &self.proof.s)
716                .field("proof_sb", &self.proof.sb)
717                .finish()
718        }
719    }
720
721    impl<S> common::TestVectorTrait for TestVector<S>
722    where
723        S: PedersenSuite + SuiteExt + std::fmt::Debug,
724    {
725        fn name() -> String {
726            S::SUITE_NAME.to_string() + "_pedersen"
727        }
728
729        fn new(comment: &str, seed: &[u8; 32], alpha: &[u8], ad: &[u8]) -> Self {
730            use super::Prover;
731            let base = common::TestVector::new(comment, seed, alpha, ad);
732            let io = VrfIo {
733                input: Input::<S>::from_affine_unchecked(base.h),
734                output: Output::from_affine_unchecked(base.gamma),
735            };
736            let secret = Secret::from_scalar(base.sk);
737            let (proof, blind) = secret.prove(io, ad);
738            Self { base, blind, proof }
739        }
740
741        fn from_map(map: &common::TestVectorMap) -> Self {
742            let base = common::TestVector::from_map(map);
743            let blind = common::scalar_decode::<S>(&map.get_bytes("blinding"));
744            let pk_com = common::point_decode::<S>(&map.get_bytes("proof_pk_com")).unwrap();
745            let r = common::point_decode::<S>(&map.get_bytes("proof_r")).unwrap();
746            let ok = common::point_decode::<S>(&map.get_bytes("proof_ok")).unwrap();
747            let s = common::scalar_decode::<S>(&map.get_bytes("proof_s"));
748            let sb = common::scalar_decode::<S>(&map.get_bytes("proof_sb"));
749            let proof = Proof {
750                pk_com,
751                r,
752                ok,
753                s,
754                sb,
755            };
756            Self { base, blind, proof }
757        }
758
759        fn to_map(&self) -> common::TestVectorMap {
760            let items = [
761                (
762                    "blinding",
763                    hex::encode(common::scalar_encode::<S>(&self.blind)),
764                ),
765                (
766                    "proof_pk_com",
767                    hex::encode(common::point_encode::<S>(&self.proof.pk_com)),
768                ),
769                (
770                    "proof_r",
771                    hex::encode(common::point_encode::<S>(&self.proof.r)),
772                ),
773                (
774                    "proof_ok",
775                    hex::encode(common::point_encode::<S>(&self.proof.ok)),
776                ),
777                (
778                    "proof_s",
779                    hex::encode(common::scalar_encode::<S>(&self.proof.s)),
780                ),
781                (
782                    "proof_sb",
783                    hex::encode(common::scalar_encode::<S>(&self.proof.sb)),
784                ),
785            ];
786            let mut map = self.base.to_map();
787            items.into_iter().for_each(|(name, value)| {
788                map.0.insert(name.to_string(), value);
789            });
790            map
791        }
792
793        fn run(&self) {
794            self.base.run();
795            let io = VrfIo {
796                input: Input::<S>::from_affine_unchecked(self.base.h),
797                output: Output::from_affine_unchecked(self.base.gamma),
798            };
799            let sk = Secret::from_scalar(self.base.sk);
800            let (proof, blind) = sk.prove(io, &self.base.ad);
801            assert_eq!(self.blind, blind, "Blinding factor mismatch");
802            assert_eq!(self.proof.pk_com, proof.pk_com, "Proof pkb mismatch");
803            assert_eq!(self.proof.r, proof.r, "Proof r mismatch");
804            assert_eq!(self.proof.ok, proof.ok, "Proof ok mismatch");
805            assert_eq!(self.proof.s, proof.s, "Proof s mismatch");
806            assert_eq!(self.proof.sb, proof.sb, "Proof sb mismatch");
807
808            assert!(Public::verify(io, &self.base.ad, &proof).is_ok());
809        }
810    }
811}