1use crate::utils::transcript::Transcript;
8use crate::*;
9use ark_ec::AffineRepr;
10use ark_ff::PrimeField;
11use core::iter::Chain;
12
13#[cfg(not(feature = "std"))]
14use ark_std::vec::Vec;
15
16pub(crate) const SECURITY_PARAMETER: usize = 128;
25
26const STACK_BUF_SIZE: usize = 128;
28
29macro_rules! stack_buf {
35 ($name:ident, $len:expr) => {
36 let _sb_len: usize = $len;
37 assert!(
38 _sb_len <= STACK_BUF_SIZE,
39 "requested {_sb_len} bytes exceeds STACK_BUF_SIZE ({STACK_BUF_SIZE})"
40 );
41 let mut _sb_backing = [0u8; STACK_BUF_SIZE];
42 let $name = &mut _sb_backing[.._sb_len];
43 };
44}
45
46pub const CHALLENGE_LEN: usize = SECURITY_PARAMETER / 8;
48
49pub const fn expanded_scalar_len<S: Suite>(sec_bits: usize) -> usize {
58 let base_field_size_in_bits = ScalarField::<S>::MODULUS_BIT_SIZE as usize;
60 let base_field_size_with_security_padding_in_bits = base_field_size_in_bits + sec_bits;
62 base_field_size_with_security_padding_in_bits.div_ceil(8)
64}
65
66pub fn nonce_scalar<S: Suite>(t: &mut S::Transcript) -> ScalarField<S> {
67 stack_buf!(buf, expanded_scalar_len::<S>(SECURITY_PARAMETER));
68 t.squeeze_raw(buf);
69 ScalarField::<S>::from_le_bytes_mod_order(buf)
70}
71
72pub fn challenge_scalar<S: Suite>(t: &mut S::Transcript) -> ScalarField<S> {
73 let mut buf = [0u8; SECURITY_PARAMETER / 8];
74 t.squeeze_raw(&mut buf);
75 ScalarField::<S>::from_le_bytes_mod_order(&buf)
76}
77
78#[derive(Clone)]
83pub struct ExactChain<A, B>(Chain<A, B>, usize);
84
85impl<A, B> ExactChain<A, B>
86where
87 A: ExactSizeIterator,
88 B: ExactSizeIterator<Item = A::Item>,
89{
90 pub fn new(a: A, b: B) -> Self {
91 let len = a.len() + b.len();
92 Self(a.chain(b), len)
93 }
94}
95
96impl<A, B> Iterator for ExactChain<A, B>
97where
98 A: Iterator,
99 B: Iterator<Item = A::Item>,
100{
101 type Item = A::Item;
102
103 fn next(&mut self) -> Option<Self::Item> {
104 let item = self.0.next();
105 if item.is_some() {
106 self.1 -= 1;
107 }
108 item
109 }
110
111 fn size_hint(&self) -> (usize, Option<usize>) {
112 (self.1, Some(self.1))
113 }
114}
115
116impl<A, B> ExactSizeIterator for ExactChain<A, B>
117where
118 A: Iterator,
119 B: Iterator<Item = A::Item>,
120{
121}
122
123#[repr(u8)]
129pub(crate) enum DomSep {
130 TinyVrf = 0x00,
132 ThinVrf = 0x01,
134 PedersenVrf = 0x02,
136 NonceExpand = 0x10,
138 Nonce = 0x11,
140 PedersenBlinding = 0x12,
142 PointToHash = 0x20,
144 Delinearize = 0x30,
146 Challenge = 0x40,
148 BatchVerify = 0x50,
150 HashToCurve = 0x60,
152}
153
154fn vrf_transcript_base<S: Suite>(
160 scheme: DomSep,
161 ios: impl ExactSizeIterator<Item = VrfIo<S>> + Clone,
162 ad: impl AsRef<[u8]>,
163) -> (S::Transcript, DelinearizeScalars<S>, usize) {
164 let n = ios.len();
165 let mut t = S::Transcript::new(S::SUITE_ID);
166 t.absorb_raw(&[scheme as u8]);
167 absorb_ios::<S>(&mut t, ios);
168 let ad = ad.as_ref();
169 t.absorb_raw(&(ad.len() as u64).to_le_bytes());
170 t.absorb_raw(ad);
171 let scalars = DelinearizeScalars::new(t.clone());
172 (t, scalars, n)
173}
174
175pub(crate) fn vrf_transcript_from_iter<S: Suite>(
182 scheme: DomSep,
183 ios: impl ExactSizeIterator<Item = VrfIo<S>> + Clone,
184 ad: impl AsRef<[u8]>,
185) -> (S::Transcript, VrfIo<S>) {
186 let n = ios.len();
187 let (t, scalars, _) = vrf_transcript_base(scheme, ios.clone(), ad);
188
189 let zero = AffinePoint::<S>::zero();
190 let io = if n == 0 {
191 VrfIo {
192 input: Input(zero),
193 output: Output(zero),
194 }
195 } else if n == 1 {
196 ios.clone().next().expect("len is 1 but iterator is empty")
197 } else {
198 merge_ios(ios, scalars)
199 };
200
201 (t, io)
202}
203
204pub(crate) fn vrf_transcript_scalars_from_iter<S: Suite>(
211 scheme: DomSep,
212 ios: impl ExactSizeIterator<Item = VrfIo<S>> + Clone,
213 ad: impl AsRef<[u8]>,
214) -> (S::Transcript, Vec<ScalarField<S>>) {
215 let (t, mut scalars, n) = vrf_transcript_base(scheme, ios, ad);
216 (t, scalars.take(n))
217}
218
219pub(crate) fn vrf_transcript<S: Suite>(
220 scheme: DomSep,
221 ios: impl AsRef<[VrfIo<S>]>,
222 ad: impl AsRef<[u8]>,
223) -> (S::Transcript, VrfIo<S>) {
224 vrf_transcript_from_iter(scheme, ios.as_ref().iter().copied(), ad)
225}
226
227fn chain_ios<'a, S: Suite>(
232 public: AffinePoint<S>,
233 ios: &'a [VrfIo<S>],
234) -> impl ExactSizeIterator<Item = VrfIo<S>> + Clone + 'a {
235 let schnorr = core::iter::once(VrfIo {
236 input: Input(S::generator()),
237 output: Output(public),
238 });
239 ExactChain::new(schnorr, ios.iter().copied())
240}
241
242pub(crate) fn vrf_transcript_with_schnorr<S: Suite>(
243 scheme: DomSep,
244 public: AffinePoint<S>,
245 ios: impl AsRef<[VrfIo<S>]>,
246 ad: impl AsRef<[u8]>,
247) -> (S::Transcript, VrfIo<S>) {
248 vrf_transcript_from_iter(scheme, chain_ios(public, ios.as_ref()), ad)
249}
250
251pub(crate) fn vrf_transcript_scalars_with_schnorr<S: Suite>(
252 scheme: DomSep,
253 public: AffinePoint<S>,
254 ios: impl AsRef<[VrfIo<S>]>,
255 ad: impl AsRef<[u8]>,
256) -> (S::Transcript, Vec<ScalarField<S>>) {
257 vrf_transcript_scalars_from_iter(scheme, chain_ios(public, ios.as_ref()), ad)
258}
259
260pub fn challenge<S: Suite>(
271 pts: &[&AffinePoint<S>],
272 transcript: Option<S::Transcript>,
273) -> ScalarField<S> {
274 let mut t = transcript.unwrap_or_else(|| S::Transcript::new(S::SUITE_ID));
275 t.absorb_raw(&[DomSep::Challenge as u8]);
276 for p in pts {
277 t.absorb_serialize(*p);
278 }
279 challenge_scalar::<S>(&mut t)
280}
281
282pub fn point_to_hash<S: Suite, const N: usize>(
291 pt: &AffinePoint<S>,
292 mul_by_cofactor: bool,
293) -> [u8; N] {
294 use ark_std::borrow::Cow::*;
295 let pt = match mul_by_cofactor {
296 false => Borrowed(pt),
297 true => Owned(pt.mul_by_cofactor()),
298 };
299 let mut t = S::Transcript::new(S::SUITE_ID);
300 t.absorb_raw(&[DomSep::PointToHash as u8]);
301 t.absorb_serialize(&*pt);
302 let mut out = [0; N];
303 t.squeeze_raw(&mut out);
304 out
305}
306
307pub fn nonce<S: Suite>(sk: &ScalarField<S>, transcript: Option<S::Transcript>) -> ScalarField<S> {
314 let mut t = transcript.unwrap_or_else(|| S::Transcript::new(S::SUITE_ID));
315
316 let mut t_exp = t.clone();
318 t_exp.absorb_raw(&[DomSep::NonceExpand as u8]);
319 t_exp.absorb_serialize(sk);
320 let mut sk_hash = [0u8; 64];
321 t_exp.squeeze_raw(&mut sk_hash);
322
323 t.absorb_raw(&[DomSep::Nonce as u8]);
325 t.absorb_raw(&sk_hash);
326 sk_hash.zeroize();
327 nonce_scalar::<S>(&mut t)
328}
329
330pub(crate) struct DelinearizeScalars<S: Suite> {
336 transcript: S::Transcript,
337 first: bool,
338}
339
340impl<S: Suite> DelinearizeScalars<S> {
341 pub fn new(mut transcript: S::Transcript) -> DelinearizeScalars<S> {
347 transcript.absorb_raw(&[DomSep::Delinearize as u8]);
348 DelinearizeScalars {
349 transcript,
350 first: true,
351 }
352 }
353
354 pub fn next(&mut self) -> ScalarField<S> {
356 use ark_ff::One;
357 if self.first {
358 self.first = false;
359 ScalarField::<S>::one()
360 } else {
361 challenge_scalar::<S>(&mut self.transcript)
362 }
363 }
364
365 pub fn take(&mut self, n: usize) -> Vec<ScalarField<S>> {
367 (0..n).map(|_| self.next()).collect()
368 }
369}
370
371fn absorb_ios<S: Suite>(t: &mut S::Transcript, ios: impl ExactSizeIterator<Item = VrfIo<S>>) {
378 let n = ios.len() as u64;
379 t.absorb_raw(&n.to_le_bytes());
380 for io in ios {
381 t.absorb_serialize(&io);
382 }
383}
384
385fn merge_ios<S: Suite>(
390 iter: impl ExactSizeIterator<Item = VrfIo<S>> + Clone,
391 mut scalars: DelinearizeScalars<S>,
392) -> VrfIo<S> {
393 let n = iter.len();
394
395 const MSM_THRESHOLD: usize = 16;
398
399 let zero = AffinePoint::<S>::zero().into_group();
400 let (input, output) = if n < MSM_THRESHOLD {
401 iter.fold((zero, zero), |(h_acc, g_acc), io| {
402 let z = scalars.next();
403 (h_acc + io.input.0 * z, g_acc + io.output.0 * z)
404 })
405 } else {
406 let zs = scalars.take(n);
407 let (inputs, outputs): (Vec<_>, Vec<_>) = iter.map(|io| (io.input.0, io.output.0)).unzip();
408 use ark_ec::VariableBaseMSM;
409 type Group<S> = <AffinePoint<S> as AffineRepr>::Group;
410 let input = Group::<S>::msm_unchecked(&inputs, &zs);
411 let output = Group::<S>::msm_unchecked(&outputs, &zs);
412 (input, output)
413 };
414 let norms = CurveGroup::normalize_batch(&[input, output]);
415 VrfIo {
416 input: Input(norms[0]),
417 output: Output(norms[1]),
418 }
419}
420
421#[cfg(test)]
422mod tests {
423 use super::*;
424 use suites::testing::TestSuite;
425
426 #[test]
428 fn scheme_tag_domain_separation() {
429 use crate::{Input, Output, VrfIo};
430
431 let sk = ScalarField::<TestSuite>::from(42u64);
432 let ios: Vec<VrfIo<TestSuite>> = (0..3u8)
433 .map(|i| {
434 let input = TestSuite::data_to_point(&[i]).unwrap();
435 let output = (input * sk).into_affine();
436 VrfIo {
437 input: Input(input),
438 output: Output(output),
439 }
440 })
441 .collect();
442
443 let (_, io_tiny) = vrf_transcript::<TestSuite>(DomSep::TinyVrf, &ios, b"foo");
444 let (_, io_thin) = vrf_transcript::<TestSuite>(DomSep::ThinVrf, &ios, b"foo");
445 let (_, io_ped) = vrf_transcript::<TestSuite>(DomSep::PedersenVrf, &ios, b"foo");
446
447 assert_ne!(io_tiny, io_thin);
449 assert_ne!(io_tiny, io_ped);
450 assert_ne!(io_thin, io_ped);
451 }
452}