use crate::utils::transcript::Transcript;
use crate::*;
use ark_ec::AffineRepr;
use ark_ff::PrimeField;
use core::iter::Chain;
#[cfg(not(feature = "std"))]
use ark_std::vec::Vec;
pub(crate) const SECURITY_PARAMETER: usize = 128;
const STACK_BUF_SIZE: usize = 128;
macro_rules! stack_buf {
($name:ident, $len:expr) => {
let _sb_len: usize = $len;
assert!(
_sb_len <= STACK_BUF_SIZE,
"requested {_sb_len} bytes exceeds STACK_BUF_SIZE ({STACK_BUF_SIZE})"
);
let mut _sb_backing = [0u8; STACK_BUF_SIZE];
let $name = &mut _sb_backing[.._sb_len];
};
}
pub const CHALLENGE_LEN: usize = SECURITY_PARAMETER / 8;
pub const fn expanded_scalar_len<S: Suite>(sec_bits: usize) -> usize {
let base_field_size_in_bits = ScalarField::<S>::MODULUS_BIT_SIZE as usize;
let base_field_size_with_security_padding_in_bits = base_field_size_in_bits + sec_bits;
base_field_size_with_security_padding_in_bits.div_ceil(8)
}
pub fn nonce_scalar<S: Suite>(t: &mut S::Transcript) -> ScalarField<S> {
stack_buf!(buf, expanded_scalar_len::<S>(SECURITY_PARAMETER));
t.squeeze_raw(buf);
ScalarField::<S>::from_le_bytes_mod_order(buf)
}
pub fn challenge_scalar<S: Suite>(t: &mut S::Transcript) -> ScalarField<S> {
let mut buf = [0u8; SECURITY_PARAMETER / 8];
t.squeeze_raw(&mut buf);
ScalarField::<S>::from_le_bytes_mod_order(&buf)
}
#[derive(Clone)]
pub struct ExactChain<A, B>(Chain<A, B>, usize);
impl<A, B> ExactChain<A, B>
where
A: ExactSizeIterator,
B: ExactSizeIterator<Item = A::Item>,
{
pub fn new(a: A, b: B) -> Self {
let len = a.len() + b.len();
Self(a.chain(b), len)
}
}
impl<A, B> Iterator for ExactChain<A, B>
where
A: Iterator,
B: Iterator<Item = A::Item>,
{
type Item = A::Item;
fn next(&mut self) -> Option<Self::Item> {
let item = self.0.next();
if item.is_some() {
self.1 -= 1;
}
item
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.1, Some(self.1))
}
}
impl<A, B> ExactSizeIterator for ExactChain<A, B>
where
A: Iterator,
B: Iterator<Item = A::Item>,
{
}
#[repr(u8)]
pub(crate) enum DomSep {
TinyVrf = 0x00,
ThinVrf = 0x01,
PedersenVrf = 0x02,
NonceExpand = 0x10,
Nonce = 0x11,
PedersenBlinding = 0x12,
PointToHash = 0x20,
Delinearize = 0x30,
Challenge = 0x40,
BatchVerify = 0x50,
HashToCurve = 0x60,
}
fn vrf_transcript_base<S: Suite>(
scheme: DomSep,
ios: impl ExactSizeIterator<Item = VrfIo<S>> + Clone,
ad: impl AsRef<[u8]>,
) -> (S::Transcript, DelinearizeScalars<S>, usize) {
let n = ios.len();
let mut t = S::Transcript::new(S::SUITE_ID);
t.absorb_raw(&[scheme as u8]);
absorb_ios::<S>(&mut t, ios);
let ad = ad.as_ref();
t.absorb_raw(&(ad.len() as u64).to_le_bytes());
t.absorb_raw(ad);
let scalars = DelinearizeScalars::new(t.clone());
(t, scalars, n)
}
pub(crate) fn vrf_transcript_from_iter<S: Suite>(
scheme: DomSep,
ios: impl ExactSizeIterator<Item = VrfIo<S>> + Clone,
ad: impl AsRef<[u8]>,
) -> (S::Transcript, VrfIo<S>) {
let n = ios.len();
let (t, scalars, _) = vrf_transcript_base(scheme, ios.clone(), ad);
let zero = AffinePoint::<S>::zero();
let io = if n == 0 {
VrfIo {
input: Input(zero),
output: Output(zero),
}
} else if n == 1 {
ios.clone().next().expect("len is 1 but iterator is empty")
} else {
merge_ios(ios, scalars)
};
(t, io)
}
pub(crate) fn vrf_transcript_scalars_from_iter<S: Suite>(
scheme: DomSep,
ios: impl ExactSizeIterator<Item = VrfIo<S>> + Clone,
ad: impl AsRef<[u8]>,
) -> (S::Transcript, Vec<ScalarField<S>>) {
let (t, mut scalars, n) = vrf_transcript_base(scheme, ios, ad);
(t, scalars.take(n))
}
pub(crate) fn vrf_transcript<S: Suite>(
scheme: DomSep,
ios: impl AsRef<[VrfIo<S>]>,
ad: impl AsRef<[u8]>,
) -> (S::Transcript, VrfIo<S>) {
vrf_transcript_from_iter(scheme, ios.as_ref().iter().copied(), ad)
}
fn chain_ios<'a, S: Suite>(
public: AffinePoint<S>,
ios: &'a [VrfIo<S>],
) -> impl ExactSizeIterator<Item = VrfIo<S>> + Clone + 'a {
let schnorr = core::iter::once(VrfIo {
input: Input(S::generator()),
output: Output(public),
});
ExactChain::new(schnorr, ios.iter().copied())
}
pub(crate) fn vrf_transcript_with_schnorr<S: Suite>(
scheme: DomSep,
public: AffinePoint<S>,
ios: impl AsRef<[VrfIo<S>]>,
ad: impl AsRef<[u8]>,
) -> (S::Transcript, VrfIo<S>) {
vrf_transcript_from_iter(scheme, chain_ios(public, ios.as_ref()), ad)
}
pub(crate) fn vrf_transcript_scalars_with_schnorr<S: Suite>(
scheme: DomSep,
public: AffinePoint<S>,
ios: impl AsRef<[VrfIo<S>]>,
ad: impl AsRef<[u8]>,
) -> (S::Transcript, Vec<ScalarField<S>>) {
vrf_transcript_scalars_from_iter(scheme, chain_ios(public, ios.as_ref()), ad)
}
pub fn challenge<S: Suite>(
pts: &[&AffinePoint<S>],
transcript: Option<S::Transcript>,
) -> ScalarField<S> {
let mut t = transcript.unwrap_or_else(|| S::Transcript::new(S::SUITE_ID));
t.absorb_raw(&[DomSep::Challenge as u8]);
for p in pts {
t.absorb_serialize(*p);
}
challenge_scalar::<S>(&mut t)
}
pub fn point_to_hash<S: Suite, const N: usize>(
pt: &AffinePoint<S>,
mul_by_cofactor: bool,
) -> [u8; N] {
use ark_std::borrow::Cow::*;
let pt = match mul_by_cofactor {
false => Borrowed(pt),
true => Owned(pt.mul_by_cofactor()),
};
let mut t = S::Transcript::new(S::SUITE_ID);
t.absorb_raw(&[DomSep::PointToHash as u8]);
t.absorb_serialize(&*pt);
let mut out = [0; N];
t.squeeze_raw(&mut out);
out
}
pub fn nonce<S: Suite>(sk: &ScalarField<S>, transcript: Option<S::Transcript>) -> ScalarField<S> {
let mut t = transcript.unwrap_or_else(|| S::Transcript::new(S::SUITE_ID));
let mut t_exp = t.clone();
t_exp.absorb_raw(&[DomSep::NonceExpand as u8]);
t_exp.absorb_serialize(sk);
let mut sk_hash = [0u8; 64];
t_exp.squeeze_raw(&mut sk_hash);
t.absorb_raw(&[DomSep::Nonce as u8]);
t.absorb_raw(&sk_hash);
sk_hash.zeroize();
nonce_scalar::<S>(&mut t)
}
pub(crate) struct DelinearizeScalars<S: Suite> {
transcript: S::Transcript,
first: bool,
}
impl<S: Suite> DelinearizeScalars<S> {
pub fn new(mut transcript: S::Transcript) -> DelinearizeScalars<S> {
transcript.absorb_raw(&[DomSep::Delinearize as u8]);
DelinearizeScalars {
transcript,
first: true,
}
}
pub fn next(&mut self) -> ScalarField<S> {
use ark_ff::One;
if self.first {
self.first = false;
ScalarField::<S>::one()
} else {
challenge_scalar::<S>(&mut self.transcript)
}
}
pub fn take(&mut self, n: usize) -> Vec<ScalarField<S>> {
(0..n).map(|_| self.next()).collect()
}
}
fn absorb_ios<S: Suite>(t: &mut S::Transcript, ios: impl ExactSizeIterator<Item = VrfIo<S>>) {
let n = ios.len() as u64;
t.absorb_raw(&n.to_le_bytes());
for io in ios {
t.absorb_serialize(&io);
}
}
fn merge_ios<S: Suite>(
iter: impl ExactSizeIterator<Item = VrfIo<S>> + Clone,
mut scalars: DelinearizeScalars<S>,
) -> VrfIo<S> {
let n = iter.len();
const MSM_THRESHOLD: usize = 16;
let zero = AffinePoint::<S>::zero().into_group();
let (input, output) = if n < MSM_THRESHOLD {
iter.fold((zero, zero), |(h_acc, g_acc), io| {
let z = scalars.next();
(h_acc + io.input.0 * z, g_acc + io.output.0 * z)
})
} else {
let zs = scalars.take(n);
let (inputs, outputs): (Vec<_>, Vec<_>) = iter.map(|io| (io.input.0, io.output.0)).unzip();
use ark_ec::VariableBaseMSM;
type Group<S> = <AffinePoint<S> as AffineRepr>::Group;
let input = Group::<S>::msm_unchecked(&inputs, &zs);
let output = Group::<S>::msm_unchecked(&outputs, &zs);
(input, output)
};
let norms = CurveGroup::normalize_batch(&[input, output]);
VrfIo {
input: Input(norms[0]),
output: Output(norms[1]),
}
}
#[cfg(test)]
mod tests {
use super::*;
use suites::testing::TestSuite;
#[test]
fn scheme_tag_domain_separation() {
use crate::{Input, Output, VrfIo};
let sk = ScalarField::<TestSuite>::from(42u64);
let ios: Vec<VrfIo<TestSuite>> = (0..3u8)
.map(|i| {
let input = TestSuite::data_to_point(&[i]).unwrap();
let output = (input * sk).into_affine();
VrfIo {
input: Input(input),
output: Output(output),
}
})
.collect();
let (_, io_tiny) = vrf_transcript::<TestSuite>(DomSep::TinyVrf, &ios, b"foo");
let (_, io_thin) = vrf_transcript::<TestSuite>(DomSep::ThinVrf, &ios, b"foo");
let (_, io_ped) = vrf_transcript::<TestSuite>(DomSep::PedersenVrf, &ios, b"foo");
assert_ne!(io_tiny, io_thin);
assert_ne!(io_tiny, io_ped);
assert_ne!(io_thin, io_ped);
}
}