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)]
125pub(crate) enum DomSep {
126 TinyVrf = 0x00,
127 ThinVrf = 0x01,
128 PedersenVrf = 0x02,
129 NonceExpand = 0x10,
130 Nonce = 0x11,
131 PedersenBlinding = 0x12,
132 PointToHash = 0x20,
133 Delinearize = 0x30,
134 Challenge = 0x40,
135 ThinBatch = 0x50,
136 PedersenBatch = 0x51,
137 HashToCurveTai = 0xFE,
138}
139
140fn vrf_transcript_base<S: Suite>(
146 scheme: DomSep,
147 ios: impl ExactSizeIterator<Item = VrfIo<S>> + Clone,
148 ad: impl AsRef<[u8]>,
149) -> (S::Transcript, DelinearizeScalars<S>, usize) {
150 let n = ios.len();
151 let mut t = S::Transcript::new(S::SUITE_ID);
152 t.absorb_raw(&[scheme as u8]);
153 absorb_ios::<S>(&mut t, ios);
154 let ad_len = u32::try_from(ad.as_ref().len()).expect("ad too long");
155 t.absorb_raw(&ad_len.to_le_bytes());
156 t.absorb_raw(ad.as_ref());
157 let scalars = DelinearizeScalars::new(t.clone());
158 (t, scalars, n)
159}
160
161pub(crate) fn vrf_transcript_from_iter<S: Suite>(
168 scheme: DomSep,
169 ios: impl ExactSizeIterator<Item = VrfIo<S>> + Clone,
170 ad: impl AsRef<[u8]>,
171) -> (S::Transcript, VrfIo<S>) {
172 let n = ios.len();
173 let (t, scalars, _) = vrf_transcript_base(scheme, ios.clone(), ad);
174
175 let zero = AffinePoint::<S>::zero();
176 let io = if n == 0 {
177 VrfIo {
178 input: Input(zero),
179 output: Output(zero),
180 }
181 } else if n == 1 {
182 ios.clone().next().expect("len is 1 but iterator is empty")
183 } else {
184 merge_ios(ios, scalars)
185 };
186
187 (t, io)
188}
189
190pub(crate) fn vrf_transcript_scalars_from_iter<S: Suite>(
197 scheme: DomSep,
198 ios: impl ExactSizeIterator<Item = VrfIo<S>> + Clone,
199 ad: impl AsRef<[u8]>,
200) -> (S::Transcript, Vec<ScalarField<S>>) {
201 let (t, mut scalars, n) = vrf_transcript_base(scheme, ios, ad);
202 (t, scalars.take(n))
203}
204
205pub(crate) fn vrf_transcript<S: Suite>(
206 scheme: DomSep,
207 ios: impl AsRef<[VrfIo<S>]>,
208 ad: impl AsRef<[u8]>,
209) -> (S::Transcript, VrfIo<S>) {
210 vrf_transcript_from_iter(scheme, ios.as_ref().iter().copied(), ad)
211}
212
213fn chain_ios<'a, S: Suite>(
218 public: AffinePoint<S>,
219 ios: &'a [VrfIo<S>],
220) -> impl ExactSizeIterator<Item = VrfIo<S>> + Clone + 'a {
221 let schnorr = core::iter::once(VrfIo {
222 input: Input(S::generator()),
223 output: Output(public),
224 });
225 ExactChain::new(schnorr, ios.iter().copied())
226}
227
228pub(crate) fn vrf_transcript_with_schnorr<S: Suite>(
229 scheme: DomSep,
230 public: AffinePoint<S>,
231 ios: impl AsRef<[VrfIo<S>]>,
232 ad: impl AsRef<[u8]>,
233) -> (S::Transcript, VrfIo<S>) {
234 vrf_transcript_from_iter(scheme, chain_ios(public, ios.as_ref()), ad)
235}
236
237pub(crate) fn vrf_transcript_scalars_with_schnorr<S: Suite>(
238 scheme: DomSep,
239 public: AffinePoint<S>,
240 ios: impl AsRef<[VrfIo<S>]>,
241 ad: impl AsRef<[u8]>,
242) -> (S::Transcript, Vec<ScalarField<S>>) {
243 vrf_transcript_scalars_from_iter(scheme, chain_ios(public, ios.as_ref()), ad)
244}
245
246pub fn challenge<S: Suite>(
257 pts: &[&AffinePoint<S>],
258 transcript: Option<S::Transcript>,
259) -> ScalarField<S> {
260 let mut t = transcript.unwrap_or_else(|| S::Transcript::new(S::SUITE_ID));
261 t.absorb_raw(&[DomSep::Challenge as u8]);
262 for p in pts {
263 t.absorb_serialize(*p);
264 }
265 challenge_scalar::<S>(&mut t)
266}
267
268pub fn point_to_hash<S: Suite, const N: usize>(
277 pt: &AffinePoint<S>,
278 mul_by_cofactor: bool,
279) -> [u8; N] {
280 use ark_std::borrow::Cow::*;
281 let pt = match mul_by_cofactor {
282 false => Borrowed(pt),
283 true => Owned(pt.mul_by_cofactor()),
284 };
285 let mut t = S::Transcript::new(S::SUITE_ID);
286 t.absorb_raw(&[DomSep::PointToHash as u8]);
287 t.absorb_serialize(&*pt);
288 let mut out = [0; N];
289 t.squeeze_raw(&mut out);
290 out
291}
292
293pub fn nonce<S: Suite>(sk: &ScalarField<S>, transcript: Option<S::Transcript>) -> ScalarField<S> {
300 let mut t = transcript.unwrap_or_else(|| S::Transcript::new(S::SUITE_ID));
301
302 let mut t_exp = t.clone();
304 t_exp.absorb_raw(&[DomSep::NonceExpand as u8]);
305 t_exp.absorb_serialize(sk);
306 let mut sk_hash = [0u8; 64];
307 t_exp.squeeze_raw(&mut sk_hash);
308
309 t.absorb_raw(&[DomSep::Nonce as u8]);
311 t.absorb_raw(&sk_hash);
312 sk_hash.zeroize();
313 nonce_scalar::<S>(&mut t)
314}
315
316pub(crate) struct DelinearizeScalars<S: Suite> {
322 transcript: S::Transcript,
323 first: bool,
324}
325
326impl<S: Suite> DelinearizeScalars<S> {
327 pub fn new(mut transcript: S::Transcript) -> DelinearizeScalars<S> {
333 transcript.absorb_raw(&[DomSep::Delinearize as u8]);
334 DelinearizeScalars {
335 transcript,
336 first: true,
337 }
338 }
339
340 pub fn next(&mut self) -> ScalarField<S> {
342 use ark_ff::One;
343 if self.first {
344 self.first = false;
345 ScalarField::<S>::one()
346 } else {
347 challenge_scalar::<S>(&mut self.transcript)
348 }
349 }
350
351 pub fn take(&mut self, n: usize) -> Vec<ScalarField<S>> {
353 (0..n).map(|_| self.next()).collect()
354 }
355}
356
357fn absorb_ios<S: Suite>(t: &mut S::Transcript, ios: impl ExactSizeIterator<Item = VrfIo<S>>) {
364 let n = u32::try_from(ios.len()).expect("too many ios");
365 t.absorb_raw(&n.to_le_bytes());
366 for io in ios {
367 t.absorb_serialize(&io);
368 }
369}
370
371fn merge_ios<S: Suite>(
376 iter: impl ExactSizeIterator<Item = VrfIo<S>> + Clone,
377 mut scalars: DelinearizeScalars<S>,
378) -> VrfIo<S> {
379 let n = iter.len();
380
381 const MSM_THRESHOLD: usize = 16;
384
385 let zero = AffinePoint::<S>::zero().into_group();
386 let (input, output) = if n < MSM_THRESHOLD {
387 iter.fold((zero, zero), |(h_acc, g_acc), io| {
388 let z = scalars.next();
389 (h_acc + io.input.0 * z, g_acc + io.output.0 * z)
390 })
391 } else {
392 let zs = scalars.take(n);
393 let (inputs, outputs): (Vec<_>, Vec<_>) = iter.map(|io| (io.input.0, io.output.0)).unzip();
394 use ark_ec::VariableBaseMSM;
395 type Group<S> = <AffinePoint<S> as AffineRepr>::Group;
396 let input = Group::<S>::msm_unchecked(&inputs, &zs);
397 let output = Group::<S>::msm_unchecked(&outputs, &zs);
398 (input, output)
399 };
400 let norms = CurveGroup::normalize_batch(&[input, output]);
401 VrfIo {
402 input: Input(norms[0]),
403 output: Output(norms[1]),
404 }
405}
406
407#[cfg(test)]
408mod tests {
409 use super::*;
410 use suites::testing::TestSuite;
411
412 #[test]
414 fn scheme_tag_domain_separation() {
415 use crate::{Input, Output, VrfIo};
416
417 let sk = ScalarField::<TestSuite>::from(42u64);
418 let ios: Vec<VrfIo<TestSuite>> = (0..3u8)
419 .map(|i| {
420 let input = TestSuite::data_to_point(&[i]).unwrap();
421 let output = (input * sk).into_affine();
422 VrfIo {
423 input: Input(input),
424 output: Output(output),
425 }
426 })
427 .collect();
428
429 let (_, io_tiny) = vrf_transcript::<TestSuite>(DomSep::TinyVrf, &ios, b"foo");
430 let (_, io_thin) = vrf_transcript::<TestSuite>(DomSep::ThinVrf, &ios, b"foo");
431 let (_, io_ped) = vrf_transcript::<TestSuite>(DomSep::PedersenVrf, &ios, b"foo");
432
433 assert_ne!(io_tiny, io_thin);
435 assert_ne!(io_tiny, io_ped);
436 assert_ne!(io_thin, io_ped);
437 }
438}