use crate::Suite;
use crate::utils;
use crate::utils::common::DomSep;
use crate::utils::straus::short_msm;
use crate::*;
use ark_ec::VariableBaseMSM;
pub const PEDERSEN_BLINDING_BASE_SEED: &[u8] = b"pedersen-blinding";
pub trait PedersenSuite: Suite {
const BLINDING_BASE: AffinePoint<Self>;
fn blinding(secret: &ScalarField<Self>, mut transcript: Self::Transcript) -> ScalarField<Self> {
transcript.absorb_raw(&[DomSep::PedersenBlinding as u8]);
Self::nonce(secret, Some(transcript))
}
}
#[derive(Debug, Clone, CanonicalSerialize, CanonicalDeserialize)]
pub struct Proof<S: PedersenSuite> {
pk_com: AffinePoint<S>,
r: AffinePoint<S>,
ok: AffinePoint<S>,
s: ScalarField<S>,
sb: ScalarField<S>,
}
impl<S: PedersenSuite> Proof<S> {
pub fn key_commitment(&self) -> AffinePoint<S> {
self.pk_com
}
}
pub trait Prover<S: PedersenSuite> {
fn prove(
&self,
ios: impl AsRef<[VrfIo<S>]>,
ad: impl AsRef<[u8]>,
) -> (Proof<S>, ScalarField<S>);
}
pub trait Verifier<S: PedersenSuite> {
fn verify(
ios: impl AsRef<[VrfIo<S>]>,
ad: impl AsRef<[u8]>,
proof: &Proof<S>,
) -> Result<(), Error>;
}
impl<S: PedersenSuite> Prover<S> for Secret<S> {
fn prove(
&self,
ios: impl AsRef<[VrfIo<S>]>,
ad: impl AsRef<[u8]>,
) -> (Proof<S>, ScalarField<S>) {
let (mut t, io) = utils::vrf_transcript::<S>(DomSep::PedersenVrf, ios, ad);
let blinding = S::blinding(&self.scalar, t.clone());
let bb = smul!(S::BLINDING_BASE, blinding);
let pk_com = (self.public.0.into_group() + bb).into_affine();
t.absorb_serialize(&pk_com);
let k = S::nonce(&self.scalar, Some(t.clone()));
let kb = S::nonce(&blinding, Some(t.clone()));
let kg = smul!(S::generator(), k);
let kbb = smul!(S::BLINDING_BASE, kb);
let r = kg + kbb;
let ok = smul!(io.input.0, k);
let norms = CurveGroup::normalize_batch(&[r, ok]);
let (r, ok) = (norms[0], norms[1]);
let c = S::challenge(&[&r, &ok], Some(t));
let s = k + c * self.scalar;
let sb = kb + c * blinding;
let proof = Proof {
pk_com,
r,
ok,
s,
sb,
};
(proof, blinding)
}
}
impl<S: PedersenSuite> Verifier<S> for Public<S> {
fn verify(
ios: impl AsRef<[VrfIo<S>]>,
ad: impl AsRef<[u8]>,
proof: &Proof<S>,
) -> Result<(), Error> {
let Proof {
pk_com,
r,
ok,
s,
sb,
} = proof;
let (mut t, io) = utils::vrf_transcript::<S>(DomSep::PedersenVrf, ios, ad);
t.absorb_serialize(pk_com);
let c = S::challenge(&[r, ok], Some(t));
let neg_c = -c;
let lhs1 = short_msm(&[io.input.0, io.output.0], &[*s, neg_c], 2);
if lhs1 != ok.into_group() {
return Err(Error::VerificationFailure);
}
let lhs2 = short_msm(
&[S::generator(), S::BLINDING_BASE, *pk_com],
&[*s, *sb, neg_c],
1,
);
if lhs2 != r.into_group() {
return Err(Error::VerificationFailure);
}
Ok(())
}
}
pub struct BatchItem<S: PedersenSuite> {
c: ScalarField<S>,
input: AffinePoint<S>,
output: AffinePoint<S>,
pk_com: AffinePoint<S>,
r: AffinePoint<S>,
ok: AffinePoint<S>,
s: ScalarField<S>,
sb: ScalarField<S>,
}
impl<S: PedersenSuite> BatchItem<S> {
pub fn new(ios: impl AsRef<[VrfIo<S>]>, ad: impl AsRef<[u8]>, proof: &Proof<S>) -> Self {
let (mut t, io) = utils::vrf_transcript::<S>(DomSep::PedersenVrf, ios, ad);
t.absorb_serialize(&proof.pk_com);
let c = S::challenge(&[&proof.r, &proof.ok], Some(t));
Self {
c,
input: io.input.0,
output: io.output.0,
pk_com: proof.pk_com,
r: proof.r,
ok: proof.ok,
s: proof.s,
sb: proof.sb,
}
}
}
pub struct BatchVerifier<S: PedersenSuite> {
items: Vec<BatchItem<S>>,
}
impl<S: PedersenSuite> Default for BatchVerifier<S> {
fn default() -> Self {
Self { items: Vec::new() }
}
}
impl<S: PedersenSuite> BatchVerifier<S> {
pub fn new() -> Self {
Self::default()
}
pub fn push_prepared(&mut self, entry: BatchItem<S>) {
self.items.push(entry);
}
pub fn push(&mut self, ios: impl AsRef<[VrfIo<S>]>, ad: impl AsRef<[u8]>, proof: &Proof<S>) {
self.push_prepared(BatchItem::new(ios, ad, proof));
}
pub fn verify(&self) -> Result<(), Error> {
let items = &self.items;
if items.is_empty() {
return Ok(());
}
let n = items.len();
let mut t = S::Transcript::new(S::SUITE_ID);
t.absorb_raw(&[DomSep::BatchVerify as u8]);
for e in items {
t.absorb_serialize(&e.c);
t.absorb_serialize(&e.s);
t.absorb_serialize(&e.sb);
}
let random_scalars: Vec<(ScalarField<S>, ScalarField<S>)> = (0..n)
.map(|_| {
let mut buf = [0u8; 32];
t.squeeze_raw(&mut buf);
let t = ScalarField::<S>::from_le_bytes_mod_order(&buf[..16]);
let u = ScalarField::<S>::from_le_bytes_mod_order(&buf[16..]);
(t, u)
})
.collect();
let mut bases = Vec::with_capacity(5 * n + 2);
let mut scalars = Vec::with_capacity(5 * n + 2);
let mut g_scalar = ScalarField::<S>::zero();
let mut b_scalar = ScalarField::<S>::zero();
for (e, (t, u)) in items.iter().zip(random_scalars.iter()) {
bases.push(e.output);
scalars.push(*t * e.c);
bases.push(e.ok);
scalars.push(*t);
bases.push(e.input);
scalars.push(-(*t * e.s));
bases.push(e.pk_com);
scalars.push(*u * e.c);
bases.push(e.r);
scalars.push(*u);
g_scalar += *u * e.s;
b_scalar += *u * e.sb;
}
bases.push(S::generator());
scalars.push(-g_scalar);
bases.push(S::BLINDING_BASE);
scalars.push(-b_scalar);
let result = <S::Affine as AffineRepr>::Group::msm_unchecked(&bases, &scalars);
if !result.is_zero() {
return Err(Error::VerificationFailure);
}
Ok(())
}
}
#[cfg(test)]
pub(crate) mod testing {
use super::*;
use crate::testing::{self as common, CheckPoint, SuiteExt, TEST_SEED, random_val};
pub fn prove_verify<S: PedersenSuite>() {
use pedersen::{Prover, Verifier};
let secret = Secret::<S>::from_seed(TEST_SEED);
let input = Input::from_affine_unchecked(random_val(None));
let io = secret.vrf_io(input);
let (proof, blinding) = secret.prove(io, b"foo");
let result = Public::verify(io, b"foo", &proof);
assert!(result.is_ok());
assert_eq!(
proof.key_commitment(),
(secret.public().0 + S::BLINDING_BASE * blinding).into()
);
}
pub fn batch_verify<S: PedersenSuite>() {
use pedersen::{BatchItem, BatchVerifier, Prover, Verifier};
let secret = Secret::<S>::from_seed(TEST_SEED);
let input = Input::from_affine_unchecked(random_val(None));
let io = secret.vrf_io(input);
let (proof1, _) = secret.prove(io, b"foo");
let (proof2, _) = secret.prove(io, b"bar");
assert!(Public::verify(io, b"foo", &proof1).is_ok());
assert!(Public::verify(io, b"bar", &proof2).is_ok());
let mut batch = BatchVerifier::new();
batch.push(io, b"foo", &proof1);
batch.push(io, b"bar", &proof2);
assert!(batch.verify().is_ok());
let mut batch = BatchVerifier::new();
let entry1 = BatchItem::new(io, b"foo", &proof1);
let entry2 = BatchItem::new(io, b"bar", &proof2);
batch.push_prepared(entry1);
batch.push_prepared(entry2);
assert!(batch.verify().is_ok());
let batch = BatchVerifier::<S>::new();
assert!(batch.verify().is_ok());
let mut batch = BatchVerifier::new();
batch.push(io, b"foo", &proof1);
batch.push(io, b"wrong", &proof2);
assert!(batch.verify().is_err());
}
pub fn prove_verify_multi_single<S: PedersenSuite>() {
use pedersen::{Prover, Verifier};
let secret = Secret::<S>::from_seed(TEST_SEED);
let input = Input::from_affine_unchecked(random_val(None));
let io = secret.vrf_io(input);
let (proof_single, blinding_single) = secret.prove(io, b"foo");
let (proof_slice, blinding_slice) = secret.prove([io], b"foo");
let encode = |p: &pedersen::Proof<S>| {
let mut buf = Vec::new();
p.serialize_compressed(&mut buf).unwrap();
buf
};
assert_eq!(encode(&proof_single), encode(&proof_slice));
assert_eq!(blinding_single, blinding_slice);
assert!(Public::verify(io, b"foo", &proof_slice).is_ok());
assert!(Public::verify([io], b"foo", &proof_single).is_ok());
}
pub fn prove_verify_multi<S: PedersenSuite>() {
use pedersen::{Prover, Verifier};
let secret = Secret::<S>::from_seed(TEST_SEED);
let mut ios: Vec<VrfIo<S>> = (0..3u8)
.map(|i| {
let input = Input::new(&[i + 1]).unwrap();
secret.vrf_io(input)
})
.collect();
ios.push(VrfIo {
input: Input(S::Affine::generator()),
output: Output(secret.public().0),
});
let (proof, _) = secret.prove(&ios[..], b"bar");
assert!(Public::verify(&ios[..], b"bar", &proof).is_ok());
let mut bad_ios = ios.clone();
bad_ios[1].output = secret.output(ios[0].input);
assert!(Public::verify(&bad_ios[..], b"bar", &proof).is_err());
let mut bad_ios = ios.clone();
bad_ios[0].input = ios[1].input;
assert!(Public::verify(&bad_ios[..], b"bar", &proof).is_err());
assert!(Public::verify(&ios[..], b"baz", &proof).is_err());
}
pub fn prove_verify_multi_empty<S: PedersenSuite>() {
use pedersen::{Prover, Verifier};
let secret = Secret::<S>::from_seed(TEST_SEED);
let ios: [VrfIo<S>; 0] = [];
let (proof, _) = secret.prove(ios, b"bar");
assert!(Public::verify(ios, b"bar", &proof).is_ok());
assert!(Public::verify(ios, b"baz", &proof).is_err());
}
pub fn blinding_base_check<S: PedersenSuite>()
where
AffinePoint<S>: CheckPoint,
{
assert_eq!(
S::BLINDING_BASE,
S::data_to_point(PEDERSEN_BLINDING_BASE_SEED).unwrap()
);
assert!(S::BLINDING_BASE.check(true).is_ok());
}
#[macro_export]
macro_rules! pedersen_suite_tests {
($suite:ty) => {
mod pedersen {
use super::*;
#[test]
fn prove_verify() {
$crate::pedersen::testing::prove_verify::<$suite>();
}
#[test]
fn prove_verify_multi_single() {
$crate::pedersen::testing::prove_verify_multi_single::<$suite>();
}
#[test]
fn prove_verify_multi() {
$crate::pedersen::testing::prove_verify_multi::<$suite>();
}
#[test]
fn prove_verify_multi_empty() {
$crate::pedersen::testing::prove_verify_multi_empty::<$suite>();
}
#[test]
fn batch_verify() {
$crate::pedersen::testing::batch_verify::<$suite>();
}
#[test]
fn blinding_base_check() {
$crate::pedersen::testing::blinding_base_check::<$suite>();
}
$crate::test_vectors!($crate::pedersen::testing::TestVector<$suite>);
}
};
}
pub struct TestVector<S: PedersenSuite> {
pub base: common::TestVector<S>,
pub blind: ScalarField<S>,
pub proof: Proof<S>,
}
impl<S: PedersenSuite> core::fmt::Debug for TestVector<S> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("TestVector")
.field("base", &self.base)
.field("blinding", &self.blind)
.field("proof_pk_com", &self.proof.pk_com)
.field("proof_r", &self.proof.r)
.field("proof_ok", &self.proof.ok)
.field("proof_s", &self.proof.s)
.field("proof_sb", &self.proof.sb)
.finish()
}
}
impl<S> common::TestVectorTrait for TestVector<S>
where
S: PedersenSuite + SuiteExt + std::fmt::Debug,
{
fn name() -> String {
S::SUITE_NAME.to_string() + "_pedersen"
}
fn new(comment: &str, seed: &[u8; 32], alpha: &[u8], ad: &[u8]) -> Self {
use super::Prover;
let base = common::TestVector::new(comment, seed, alpha, ad);
let io = VrfIo {
input: Input::<S>::from_affine_unchecked(base.h),
output: Output::from_affine_unchecked(base.gamma),
};
let secret = Secret::from_scalar(base.sk);
let (proof, blind) = secret.prove(io, ad);
Self { base, blind, proof }
}
fn from_map(map: &common::TestVectorMap) -> Self {
let base = common::TestVector::from_map(map);
let blind = common::scalar_decode::<S>(&map.get_bytes("blinding"));
let pk_com = common::point_decode::<S>(&map.get_bytes("proof_pk_com")).unwrap();
let r = common::point_decode::<S>(&map.get_bytes("proof_r")).unwrap();
let ok = common::point_decode::<S>(&map.get_bytes("proof_ok")).unwrap();
let s = common::scalar_decode::<S>(&map.get_bytes("proof_s"));
let sb = common::scalar_decode::<S>(&map.get_bytes("proof_sb"));
let proof = Proof {
pk_com,
r,
ok,
s,
sb,
};
Self { base, blind, proof }
}
fn to_map(&self) -> common::TestVectorMap {
let items = [
(
"blinding",
hex::encode(common::scalar_encode::<S>(&self.blind)),
),
(
"proof_pk_com",
hex::encode(common::point_encode::<S>(&self.proof.pk_com)),
),
(
"proof_r",
hex::encode(common::point_encode::<S>(&self.proof.r)),
),
(
"proof_ok",
hex::encode(common::point_encode::<S>(&self.proof.ok)),
),
(
"proof_s",
hex::encode(common::scalar_encode::<S>(&self.proof.s)),
),
(
"proof_sb",
hex::encode(common::scalar_encode::<S>(&self.proof.sb)),
),
];
let mut map = self.base.to_map();
items.into_iter().for_each(|(name, value)| {
map.0.insert(name.to_string(), value);
});
map
}
fn run(&self) {
self.base.run();
let io = VrfIo {
input: Input::<S>::from_affine_unchecked(self.base.h),
output: Output::from_affine_unchecked(self.base.gamma),
};
let sk = Secret::from_scalar(self.base.sk);
let (proof, blind) = sk.prove(io, &self.base.ad);
assert_eq!(self.blind, blind, "Blinding factor mismatch");
assert_eq!(self.proof.pk_com, proof.pk_com, "Proof pkb mismatch");
assert_eq!(self.proof.r, proof.r, "Proof r mismatch");
assert_eq!(self.proof.ok, proof.ok, "Proof ok mismatch");
assert_eq!(self.proof.s, proof.s, "Proof s mismatch");
assert_eq!(self.proof.sb, proof.sb, "Proof sb mismatch");
assert!(Public::verify(io, &self.base.ad, &proof).is_ok());
}
}
}