use std::{collections::HashMap, convert::TryFrom};
use curve25519_dalek::{
edwards::{CompressedEdwardsY, EdwardsPoint},
scalar::Scalar,
traits::{IsIdentity, VartimeMultiscalarMul},
};
use rand_core::{CryptoRng, RngCore};
use sha2::{Digest, Sha512};
use crate::{Error, Signature, VerificationKey, VerificationKeyBytes};
fn gen_u128<R: RngCore + CryptoRng>(mut rng: R) -> u128 {
let mut bytes = [0u8; 16];
rng.fill_bytes(&mut bytes[..]);
u128::from_le_bytes(bytes)
}
#[derive(Clone, Debug)]
pub struct Item {
vk_bytes: VerificationKeyBytes,
sig: Signature,
k: Scalar,
}
impl<'msg, M: AsRef<[u8]> + ?Sized> From<(VerificationKeyBytes, Signature, &'msg M)> for Item {
fn from(tup: (VerificationKeyBytes, Signature, &'msg M)) -> Self {
let (vk_bytes, sig, msg) = tup;
let k = Scalar::from_hash(
Sha512::default()
.chain(&sig.R_bytes[..])
.chain(&vk_bytes.0[..])
.chain(msg),
);
Self { vk_bytes, sig, k }
}
}
impl Item {
pub fn verify_single(self) -> Result<(), Error> {
VerificationKey::try_from(self.vk_bytes)
.and_then(|vk| vk.verify_prehashed(&self.sig, self.k))
}
}
#[derive(Default)]
pub struct Verifier {
signatures: HashMap<VerificationKeyBytes, Vec<(Scalar, Signature)>>,
batch_size: usize,
}
impl Verifier {
pub fn new() -> Verifier {
Verifier::default()
}
pub fn queue<I: Into<Item>>(&mut self, item: I) {
let Item { vk_bytes, sig, k } = item.into();
self.signatures
.entry(vk_bytes)
.or_insert_with(|| Vec::with_capacity(1))
.push((k, sig));
self.batch_size += 1;
}
#[allow(non_snake_case)]
pub fn verify<R: RngCore + CryptoRng>(self, mut rng: R) -> Result<(), Error> {
let m = self.signatures.keys().count();
let mut A_coeffs = Vec::with_capacity(m);
let mut As = Vec::with_capacity(m);
let mut R_coeffs = Vec::with_capacity(self.batch_size);
let mut Rs = Vec::with_capacity(self.batch_size);
let mut B_coeff = Scalar::zero();
for (vk_bytes, sigs) in self.signatures.iter() {
let A = CompressedEdwardsY(vk_bytes.0)
.decompress()
.ok_or(Error::InvalidSignature)?;
let mut A_coeff = Scalar::zero();
for (k, sig) in sigs.iter() {
let R = CompressedEdwardsY(sig.R_bytes)
.decompress()
.ok_or(Error::InvalidSignature)?;
let s = Scalar::from_canonical_bytes(sig.s_bytes).ok_or(Error::InvalidSignature)?;
let z = Scalar::from(gen_u128(&mut rng));
B_coeff -= z * s;
Rs.push(R);
R_coeffs.push(z);
A_coeff += z * k;
}
As.push(A);
A_coeffs.push(A_coeff);
}
use core::iter::once;
use curve25519_dalek::constants::ED25519_BASEPOINT_POINT as B;
let check = EdwardsPoint::vartime_multiscalar_mul(
once(&B_coeff).chain(A_coeffs.iter()).chain(R_coeffs.iter()),
once(&B).chain(As.iter()).chain(Rs.iter()),
);
if check.mul_by_cofactor().is_identity() {
Ok(())
} else {
Err(Error::InvalidSignature)
}
}
}