use alloc::vec::Vec;
use core::convert::TryFrom;
use group::{
cofactor::CofactorGroup,
ff::{Field, PrimeField},
GroupEncoding,
};
use rand_core::{CryptoRng, RngCore};
use crate::{private::SealedScalar, scalar_mul::VartimeMultiscalarMul, *};
fn gen_128_bits<R: RngCore + CryptoRng>(mut rng: R) -> [u64; 4] {
let mut bytes = [0u64; 4];
bytes[0] = rng.next_u64();
bytes[1] = rng.next_u64();
bytes
}
#[derive(Clone, Debug)]
enum Inner<S: SpendAuth, B: Binding<Scalar = S::Scalar, Point = S::Point>> {
SpendAuth {
vk_bytes: VerificationKeyBytes<S>,
sig: Signature<S>,
c: S::Scalar,
},
Binding {
vk_bytes: VerificationKeyBytes<B>,
sig: Signature<B>,
c: B::Scalar,
},
}
#[derive(Clone, Debug)]
pub struct Item<S: SpendAuth, B: Binding<Scalar = S::Scalar, Point = S::Point>> {
inner: Inner<S, B>,
}
impl<S: SpendAuth, B: Binding<Scalar = S::Scalar, Point = S::Point>> Item<S, B> {
pub fn from_spendauth<M: AsRef<[u8]>>(
vk_bytes: VerificationKeyBytes<S>,
sig: Signature<S>,
msg: &M,
) -> Self {
let c = HStar::<S>::default()
.update(&sig.r_bytes[..])
.update(&vk_bytes.bytes[..])
.update(msg)
.finalize();
Self {
inner: Inner::SpendAuth { vk_bytes, sig, c },
}
}
pub fn from_binding<M: AsRef<[u8]>>(
vk_bytes: VerificationKeyBytes<B>,
sig: Signature<B>,
msg: &M,
) -> Self {
let c = HStar::<B>::default()
.update(&sig.r_bytes[..])
.update(&vk_bytes.bytes[..])
.update(msg)
.finalize();
Self {
inner: Inner::Binding { vk_bytes, sig, c },
}
}
#[allow(non_snake_case)]
pub fn verify_single(self) -> Result<(), Error> {
match self.inner {
Inner::Binding { vk_bytes, sig, c } => {
VerificationKey::<B>::try_from(vk_bytes).and_then(|vk| vk.verify_prehashed(&sig, c))
}
Inner::SpendAuth { vk_bytes, sig, c } => {
VerificationKey::<S>::try_from(vk_bytes).and_then(|vk| vk.verify_prehashed(&sig, c))
}
}
}
}
pub struct Verifier<S: SpendAuth, B: Binding<Scalar = S::Scalar, Point = S::Point>> {
signatures: Vec<Item<S, B>>,
}
impl<S: SpendAuth, B: Binding<Scalar = S::Scalar, Point = S::Point>> Default for Verifier<S, B> {
fn default() -> Self {
Verifier { signatures: vec![] }
}
}
impl<S: SpendAuth, B: Binding<Scalar = S::Scalar, Point = S::Point>> Verifier<S, B> {
pub fn new() -> Verifier<S, B> {
Verifier::default()
}
pub fn queue<I: Into<Item<S, B>>>(&mut self, item: I) {
self.signatures.push(item.into());
}
#[allow(non_snake_case)]
pub fn verify<R: RngCore + CryptoRng>(self, mut rng: R) -> Result<(), Error> {
let n = self.signatures.len();
let mut VK_coeffs = Vec::with_capacity(n);
let mut VKs = Vec::with_capacity(n);
let mut R_coeffs = Vec::with_capacity(self.signatures.len());
let mut Rs = Vec::with_capacity(self.signatures.len());
let mut P_spendauth_coeff = S::Scalar::ZERO;
let mut P_binding_coeff = B::Scalar::ZERO;
for item in self.signatures.iter() {
let (s_bytes, r_bytes, c) = match item.inner {
Inner::SpendAuth { sig, c, .. } => (sig.s_bytes, sig.r_bytes, c),
Inner::Binding { sig, c, .. } => (sig.s_bytes, sig.r_bytes, c),
};
let s = {
let mut repr = <S::Scalar as PrimeField>::Repr::default();
repr.as_mut().copy_from_slice(&s_bytes);
let maybe_scalar = S::Scalar::from_repr(repr);
if maybe_scalar.is_some().into() {
maybe_scalar.unwrap()
} else {
return Err(Error::InvalidSignature);
}
};
let R = {
let mut repr = <S::Point as GroupEncoding>::Repr::default();
repr.as_mut().copy_from_slice(&r_bytes);
let maybe_point = S::Point::from_bytes(&repr);
if maybe_point.is_some().into() {
maybe_point.unwrap()
} else {
return Err(Error::InvalidSignature);
}
};
let VK = match item.inner {
Inner::SpendAuth { vk_bytes, .. } => {
VerificationKey::<S>::try_from(vk_bytes.bytes)?.point
}
Inner::Binding { vk_bytes, .. } => {
VerificationKey::<B>::try_from(vk_bytes.bytes)?.point
}
};
let z = S::Scalar::from_raw(gen_128_bits(&mut rng));
let P_coeff = z * s;
match item.inner {
Inner::SpendAuth { .. } => {
P_spendauth_coeff -= P_coeff;
}
Inner::Binding { .. } => {
P_binding_coeff -= P_coeff;
}
};
R_coeffs.push(z);
Rs.push(R);
VK_coeffs.push(S::Scalar::ZERO + (z * c));
VKs.push(VK);
}
use core::iter::once;
let scalars = once(&P_spendauth_coeff)
.chain(once(&P_binding_coeff))
.chain(VK_coeffs.iter())
.chain(R_coeffs.iter());
let basepoints = [S::basepoint(), B::basepoint()];
let points = basepoints.iter().chain(VKs.iter()).chain(Rs.iter());
let check = S::Point::vartime_multiscalar_mul(scalars, points);
if check.is_small_order().into() {
Ok(())
} else {
Err(Error::InvalidSignature)
}
}
}