1use crate::{utils::challenge_scalar, utils::common::DomSep, utils::straus::short_msm, *};
26
27pub trait ThinSuite: Suite {}
31
32impl<T> ThinSuite for T where T: Suite {}
33
34#[derive(Debug, Clone, CanonicalSerialize, CanonicalDeserialize)]
43pub struct Proof<S: ThinSuite> {
44 pub r: AffinePoint<S>,
46 pub s: ScalarField<S>,
48}
49
50#[inline(always)]
51fn vrf_transcript<S: ThinSuite>(
52 public: AffinePoint<S>,
53 ios: impl AsRef<[VrfIo<S>]>,
54 ad: impl AsRef<[u8]>,
55) -> (S::Transcript, VrfIo<S>) {
56 utils::vrf_transcript_with_schnorr(DomSep::ThinVrf, public, ios, ad)
57}
58
59#[inline(always)]
60fn vrf_transcript_scalars<S: ThinSuite>(
61 public: AffinePoint<S>,
62 ios: impl AsRef<[VrfIo<S>]>,
63 ad: impl AsRef<[u8]>,
64) -> (S::Transcript, Vec<ScalarField<S>>) {
65 utils::vrf_transcript_scalars_with_schnorr(DomSep::ThinVrf, public, ios, ad)
66}
67
68pub trait Prover<S: ThinSuite> {
70 fn prove(&self, ios: impl AsRef<[VrfIo<S>]>, ad: impl AsRef<[u8]>) -> Proof<S>;
74}
75
76pub trait Verifier<S: ThinSuite> {
96 fn verify(
104 &self,
105 ios: impl AsRef<[VrfIo<S>]>,
106 ad: impl AsRef<[u8]>,
107 proof: &Proof<S>,
108 ) -> Result<(), Error>;
109}
110
111impl<S: ThinSuite> Prover<S> for Secret<S> {
112 fn prove(&self, ios: impl AsRef<[VrfIo<S>]>, ad: impl AsRef<[u8]>) -> Proof<S> {
113 let (t, merged) = vrf_transcript::<S>(self.public.0, ios, ad);
114
115 let k = S::nonce(&self.scalar, Some(t.clone()));
117
118 let r = smul!(merged.input.0, k).into_affine();
120
121 let c = S::challenge(&[&r], Some(t));
123
124 let s = k + c * self.scalar;
126
127 Proof { r, s }
128 }
129}
130
131impl<S: ThinSuite> Verifier<S> for Public<S> {
132 fn verify(
133 &self,
134 ios: impl AsRef<[VrfIo<S>]>,
135 ad: impl AsRef<[u8]>,
136 proof: &Proof<S>,
137 ) -> Result<(), Error> {
138 if self.is_identity() {
141 return Err(Error::InvalidData);
142 }
143
144 let ios = ios.as_ref();
147 if ios.iter().any(VrfIo::has_identity) {
148 return Err(Error::InvalidData);
149 }
150
151 let Proof { r, s } = proof;
152 let (t, merged) = vrf_transcript::<S>(self.0, ios, ad);
153
154 let c = S::challenge(&[r], Some(t));
156
157 let lhs = short_msm(&[merged.input.0, merged.output.0], &[*s, -c], 2);
159 if lhs != r.into_group() {
160 return Err(Error::VerificationFailure);
161 }
162
163 Ok(())
164 }
165}
166
167pub struct BatchItem<S: ThinSuite> {
173 c: ScalarField<S>,
174 pk: Public<S>,
175 ios: Vec<VrfIo<S>>,
176 zs: Vec<ScalarField<S>>,
177 r: AffinePoint<S>,
178 s: ScalarField<S>,
179}
180
181pub struct BatchVerifier<S: ThinSuite> {
189 items: Vec<BatchItem<S>>,
190}
191
192impl<S: ThinSuite> Default for BatchVerifier<S> {
193 fn default() -> Self {
194 Self { items: Vec::new() }
195 }
196}
197
198impl<S: ThinSuite> BatchVerifier<S> {
199 pub fn new() -> Self {
201 Self::default()
202 }
203
204 pub fn prepare(
210 public: &Public<S>,
211 ios: impl AsRef<[VrfIo<S>]>,
212 ad: impl AsRef<[u8]>,
213 proof: &Proof<S>,
214 ) -> BatchItem<S> {
215 let ios = ios.as_ref();
216 let (t, zs) = vrf_transcript_scalars::<S>(public.0, ios, ad);
217 let c = S::challenge(&[&proof.r], Some(t));
218 BatchItem {
219 c,
220 pk: *public,
221 ios: ios.to_vec(),
222 zs,
223 r: proof.r,
224 s: proof.s,
225 }
226 }
227
228 pub fn push_prepared(&mut self, entry: BatchItem<S>) {
230 self.items.push(entry);
231 }
232
233 pub fn push(
235 &mut self,
236 public: &Public<S>,
237 ios: impl AsRef<[VrfIo<S>]>,
238 ad: impl AsRef<[u8]>,
239 proof: &Proof<S>,
240 ) {
241 let entry = Self::prepare(public, ios, ad, proof);
242 self.push_prepared(entry);
243 }
244
245 pub fn verify(&self) -> Result<(), Error> {
258 use ark_ec::VariableBaseMSM;
259 use ark_ff::Zero;
260
261 let items = &self.items;
262 if items.is_empty() {
263 return Ok(());
264 }
265 if items
267 .iter()
268 .any(|item| item.pk.is_identity() || item.ios.iter().any(VrfIo::has_identity))
269 {
270 return Err(Error::InvalidData);
271 }
272
273 let mut t = S::Transcript::new(S::SUITE_ID);
275 t.absorb_raw(&[DomSep::BatchVerify as u8]);
276 for e in items {
277 t.absorb_serialize(&e.c);
278 t.absorb_serialize(&e.s);
279 }
280
281 let total_points: usize = items.iter().map(|e| 2 + 2 * e.ios.len()).sum::<usize>() + 1;
283 let mut bases = Vec::with_capacity(total_points);
284 let mut scalars = Vec::with_capacity(total_points);
285 let mut g_scalar = ScalarField::<S>::zero();
286
287 for item in items.iter() {
288 let w = challenge_scalar::<S>(&mut t);
290
291 let wc = w * item.c;
292 let ws = w * item.s;
293
294 bases.push(item.r);
296 scalars.push(w);
297
298 bases.push(item.pk.0);
300 scalars.push(wc * item.zs[0]);
301
302 g_scalar -= ws * item.zs[0];
304
305 for (i, io) in item.ios.iter().enumerate() {
307 bases.push(io.output.0);
308 scalars.push(wc * item.zs[i + 1]);
309
310 bases.push(io.input.0);
311 scalars.push(-(ws * item.zs[i + 1]));
312 }
313 }
314
315 bases.push(S::generator());
317 scalars.push(g_scalar);
318
319 let result = <S::Affine as AffineRepr>::Group::msm_unchecked(&bases, &scalars);
320 if !result.is_zero() {
321 return Err(Error::VerificationFailure);
322 }
323
324 Ok(())
325 }
326}
327
328#[cfg(test)]
329pub(crate) mod testing {
330 use super::*;
331 use crate::testing::{self as common, SuiteExt, TEST_SEED, random_val};
332
333 pub fn prove_verify<S: ThinSuite>() {
334 use thin::{Prover, Verifier};
335
336 let secret = Secret::<S>::from_seed(TEST_SEED);
337 let public = secret.public();
338 let input = Input::from_affine_unchecked(random_val(None));
339 let io = secret.vrf_io(input);
340
341 let proof = secret.prove(io, b"foo");
342 let result = public.verify(io, b"foo", &proof);
343 assert!(result.is_ok());
344 }
345
346 pub fn batch_verify<S: ThinSuite>() {
347 use thin::{BatchVerifier, Prover, Verifier};
348
349 let secret = Secret::<S>::from_seed(TEST_SEED);
350 let public = secret.public();
351 let input = Input::from_affine_unchecked(random_val(None));
352 let io = secret.vrf_io(input);
353
354 let proof1 = secret.prove(io, b"foo");
355 let proof2 = secret.prove(io, b"bar");
356
357 assert!(public.verify(io, b"foo", &proof1).is_ok());
359 assert!(public.verify(io, b"bar", &proof2).is_ok());
360
361 let mut batch = BatchVerifier::new();
363 batch.push(&public, io, b"foo", &proof1);
364 batch.push(&public, io, b"bar", &proof2);
365 assert!(batch.verify().is_ok());
366
367 let mut batch = BatchVerifier::new();
369 let entry1 = BatchVerifier::prepare(&public, io, b"foo", &proof1);
370 let entry2 = BatchVerifier::prepare(&public, io, b"bar", &proof2);
371 batch.push_prepared(entry1);
372 batch.push_prepared(entry2);
373 assert!(batch.verify().is_ok());
374
375 let batch = BatchVerifier::<S>::new();
377 assert!(batch.verify().is_ok());
378
379 let mut batch = BatchVerifier::new();
381 batch.push(&public, io, b"foo", &proof1);
382 batch.push(&public, io, b"wrong", &proof2);
383 assert!(batch.verify().is_err());
384 }
385
386 pub fn prove_verify_multi_single<S: ThinSuite>() {
388 use thin::{Prover, Verifier};
389
390 let secret = Secret::<S>::from_seed(TEST_SEED);
391 let public = secret.public();
392 let input = Input::from_affine_unchecked(random_val(None));
393 let io = secret.vrf_io(input);
394
395 let proof_single = secret.prove(io, b"foo");
396 let proof_slice = secret.prove([io], b"foo");
397
398 let encode = |p: &thin::Proof<S>| {
400 let mut buf = Vec::new();
401 p.serialize_compressed(&mut buf).unwrap();
402 buf
403 };
404 assert_eq!(encode(&proof_single), encode(&proof_slice));
405
406 assert!(public.verify(io, b"foo", &proof_slice).is_ok());
408 assert!(public.verify([io], b"foo", &proof_single).is_ok());
409 }
410
411 pub fn identity_public_key_rejected<S: ThinSuite>() {
419 use thin::{BatchVerifier, Verifier};
420
421 let identity = Public::<S>(AffinePoint::<S>::zero());
422 let s = ScalarField::<S>::from(0x5eed_u64);
423 let forged = Proof::<S> {
424 r: (S::generator() * s).into_affine(),
425 s,
426 };
427
428 assert!(identity.verify([], b"forgery", &forged).is_err());
429
430 let mut batch = BatchVerifier::new();
431 batch.push(&identity, [], b"forgery", &forged);
432 assert!(batch.verify().is_err());
433 }
434
435 pub fn identity_io_pair_rejected<S: ThinSuite>() {
444 use thin::{BatchVerifier, Prover, Verifier};
445
446 let identity_io = VrfIo::<S> {
447 input: Input(AffinePoint::<S>::zero()),
448 output: Output(AffinePoint::<S>::zero()),
449 };
450
451 for seed in [common::TEST_SEED, [0x11; 32]] {
452 let secret = Secret::<S>::from_seed(seed);
453 let public = secret.public();
454
455 let proof = secret.prove([identity_io], b"forgery");
456 assert!(public.verify([identity_io], b"forgery", &proof).is_err());
457
458 let mut batch = BatchVerifier::new();
459 batch.push(&public, [identity_io], b"forgery", &proof);
460 assert!(batch.verify().is_err());
461
462 let good_io = secret.vrf_io(Input::new(b"good").unwrap());
463 let ios = [good_io, identity_io];
464 let proof = secret.prove(ios, b"forgery");
465 assert!(public.verify(ios, b"forgery", &proof).is_err());
466
467 let mut batch = BatchVerifier::new();
468 batch.push(&public, ios, b"forgery", &proof);
469 assert!(batch.verify().is_err());
470 }
471 }
472
473 pub fn prove_verify_multi<S: ThinSuite>() {
475 use thin::{Prover, Verifier};
476
477 let secret = Secret::<S>::from_seed(TEST_SEED);
478 let public = secret.public();
479
480 let ios: Vec<VrfIo<S>> = (0..3u8)
481 .map(|i| {
482 let input = Input::new(&[i + 1]).unwrap();
483 secret.vrf_io(input)
484 })
485 .collect();
486
487 let proof = secret.prove(&ios[..], b"bar");
488 assert!(public.verify(&ios[..], b"bar", &proof).is_ok());
489
490 let mut bad_ios = ios.clone();
492 bad_ios[1].output = secret.output(ios[0].input);
493 assert!(public.verify(&bad_ios[..], b"bar", &proof).is_err());
494
495 let mut bad_ios = ios.clone();
497 bad_ios[0].input = ios[1].input;
498 assert!(public.verify(&bad_ios[..], b"bar", &proof).is_err());
499
500 assert!(public.verify(&ios[..], b"baz", &proof).is_err());
502 }
503
504 pub fn prove_verify_multi_empty<S: ThinSuite>() {
506 use thin::{Prover, Verifier};
507
508 let secret = Secret::<S>::from_seed(TEST_SEED);
509 let public = secret.public();
510
511 let proof = secret.prove([], b"bar");
512 assert!(public.verify([], b"bar", &proof).is_ok());
513
514 assert!(public.verify([], b"baz", &proof).is_err());
516 }
517
518 #[macro_export]
519 macro_rules! thin_suite_tests {
520 ($suite:ty) => {
521 mod thin {
522 use super::*;
523
524 #[test]
525 fn prove_verify() {
526 $crate::thin::testing::prove_verify::<$suite>();
527 }
528
529 #[test]
530 fn prove_verify_multi_single() {
531 $crate::thin::testing::prove_verify_multi_single::<$suite>();
532 }
533
534 #[test]
535 fn prove_verify_multi() {
536 $crate::thin::testing::prove_verify_multi::<$suite>();
537 }
538
539 #[test]
540 fn prove_verify_multi_empty() {
541 $crate::thin::testing::prove_verify_multi_empty::<$suite>();
542 }
543
544 #[test]
545 fn batch_verify() {
546 $crate::thin::testing::batch_verify::<$suite>();
547 }
548
549 #[test]
550 fn identity_public_key_rejected() {
551 $crate::thin::testing::identity_public_key_rejected::<$suite>();
552 }
553
554 #[test]
555 fn identity_io_pair_rejected() {
556 $crate::thin::testing::identity_io_pair_rejected::<$suite>();
557 }
558
559 $crate::test_vectors!($crate::thin::testing::TestVector<$suite>);
560 }
561 };
562 }
563
564 pub struct TestVector<S: ThinSuite> {
565 pub base: common::TestVector<S>,
566 pub proof_r: AffinePoint<S>,
567 pub proof_s: ScalarField<S>,
568 }
569
570 impl<S: ThinSuite> core::fmt::Debug for TestVector<S> {
571 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
572 let r = hex::encode(common::point_encode::<S>(&self.proof_r));
573 let s = hex::encode(common::scalar_encode::<S>(&self.proof_s));
574 f.debug_struct("TestVector")
575 .field("base", &self.base)
576 .field("proof_r", &r)
577 .field("proof_s", &s)
578 .finish()
579 }
580 }
581
582 impl<S> common::TestVectorTrait for TestVector<S>
583 where
584 S: ThinSuite + SuiteExt + std::fmt::Debug,
585 {
586 fn name() -> String {
587 S::SUITE_NAME.to_string() + "_thin"
588 }
589
590 fn new(comment: &str, seed: &[u8; 32], alpha: &[u8], ad: &[u8]) -> Self {
591 use super::Prover;
592 let base = common::TestVector::new(comment, seed, alpha, ad);
593 let io = VrfIo {
594 input: Input::<S>::from_affine_unchecked(base.h),
595 output: Output::from_affine_unchecked(base.gamma),
596 };
597 let secret = Secret::from_scalar(base.sk);
598 let proof: Proof<S> = secret.prove(io, ad);
599 Self {
600 base,
601 proof_r: proof.r,
602 proof_s: proof.s,
603 }
604 }
605
606 fn from_map(map: &common::TestVectorMap) -> Self {
607 let base = common::TestVector::from_map(map);
608 let proof_r = common::point_decode::<S>(&map.get_bytes("proof_r")).unwrap();
609 let proof_s = common::scalar_decode::<S>(&map.get_bytes("proof_s"));
610 Self {
611 base,
612 proof_r,
613 proof_s,
614 }
615 }
616
617 fn to_map(&self) -> common::TestVectorMap {
618 let items = [
619 (
620 "proof_r",
621 hex::encode(common::point_encode::<S>(&self.proof_r)),
622 ),
623 (
624 "proof_s",
625 hex::encode(common::scalar_encode::<S>(&self.proof_s)),
626 ),
627 ];
628 let mut map = self.base.to_map();
629 items.into_iter().for_each(|(name, value)| {
630 map.0.insert(name.to_string(), value);
631 });
632 map
633 }
634
635 fn run(&self) {
636 self.base.run();
637 let io = VrfIo {
638 input: Input::<S>::from_affine_unchecked(self.base.h),
639 output: Output::from_affine_unchecked(self.base.gamma),
640 };
641 let sk = Secret::from_scalar(self.base.sk);
642 let proof = sk.prove(io, &self.base.ad);
643 assert_eq!(self.proof_r, proof.r, "Thin VRF proof R mismatch");
644 assert_eq!(self.proof_s, proof.s, "Thin VRF proof s mismatch");
645
646 let pk = Public(self.base.pk);
647 assert!(pk.verify(io, &self.base.ad, &proof).is_ok());
648 }
649 }
650
651 #[test]
657 fn known_dlog_input_forgery() {
658 use ark_ff::Field;
659
660 type S = crate::suites::testing::TestSuite;
661 type Sc = ScalarField<S>;
662
663 let g = S::generator();
664
665 let sk = Sc::from(42);
667 let pk = (g * sk).into_affine();
668
669 let d = Sc::from(7);
671 let input_pt = (g * d).into_affine();
672 let input = Input::<S>::from_affine_unchecked(input_pt);
673
674 let honest_output = (input_pt * sk).into_affine();
676
677 let t_scalar = Sc::from(1234);
679 let fake_output_pt = (g * t_scalar).into_affine();
680 assert_ne!(fake_output_pt, honest_output);
681 let fake_output = Output::<S>::from_affine_unchecked(fake_output_pt);
682
683 let ad: &[u8] = b"attack";
684 let fake_io = VrfIo {
685 input,
686 output: fake_output,
687 };
688
689 let fake_ios: &[VrfIo<S>] = &[fake_io];
691 let (transcript, zs) = vrf_transcript_scalars::<S>(pk, fake_ios, ad);
692 let (z0, z1) = (zs[0], zs[1]);
693
694 let merged_input = (g * z0 + input_pt * z1).into_affine();
696
697 let x = (z0 * sk + z1 * t_scalar) * (z0 + z1 * d).inverse().unwrap();
703
704 let k = Sc::from(9999);
706 let r = (merged_input * k).into_affine();
707 let c = S::challenge(&[&r], Some(transcript));
708 let s = k + c * x;
709
710 let forged_proof = Proof::<S> { r, s };
711
712 let public = Public::<S>(pk);
729 assert!(
730 public.verify(fake_io, ad, &forged_proof).is_ok(),
731 "Forged proof must verify when input discrete log is known"
732 );
733 }
734}