2021-02-25 08:06:54 -08:00
|
|
|
// -*- mode: rust; -*-
|
|
|
|
//
|
2021-03-01 06:38:25 -08:00
|
|
|
// This file is part of reddsa.
|
2021-02-25 08:06:54 -08:00
|
|
|
// Copyright (c) 2019-2021 Zcash Foundation
|
|
|
|
// See LICENSE for licensing information.
|
|
|
|
//
|
|
|
|
// Authors:
|
|
|
|
// - Deirdre Connolly <deirdre@zfnd.org>
|
|
|
|
// - Henry de Valence <hdevalence@hdevalence.ca>
|
|
|
|
|
2021-03-01 07:29:07 -08:00
|
|
|
//! Performs batch RedDSA signature verification.
|
2020-07-03 15:23:28 -07:00
|
|
|
//!
|
|
|
|
//! Batch verification asks whether *all* signatures in some set are valid,
|
|
|
|
//! rather than asking whether *each* of them is valid. This allows sharing
|
|
|
|
//! computations among all signature verifications, performing less work overall
|
|
|
|
//! at the cost of higher latency (the entire batch must complete), complexity of
|
|
|
|
//! caller code (which must assemble a batch of signatures across work-items),
|
|
|
|
//! and loss of the ability to easily pinpoint failing signatures.
|
|
|
|
//!
|
|
|
|
|
2022-05-06 07:57:45 -07:00
|
|
|
use alloc::vec::Vec;
|
|
|
|
use core::convert::TryFrom;
|
2020-07-03 15:23:28 -07:00
|
|
|
|
2021-03-01 07:29:07 -08:00
|
|
|
use group::{
|
|
|
|
cofactor::CofactorGroup,
|
|
|
|
ff::{Field, PrimeField},
|
|
|
|
GroupEncoding,
|
|
|
|
};
|
2020-07-03 15:23:28 -07:00
|
|
|
use rand_core::{CryptoRng, RngCore};
|
|
|
|
|
2021-03-01 07:29:07 -08:00
|
|
|
use crate::{private::SealedScalar, scalar_mul::VartimeMultiscalarMul, *};
|
2020-07-03 15:23:28 -07:00
|
|
|
|
|
|
|
// Shim to generate a random 128bit value in a [u64; 4], without
|
|
|
|
// importing `rand`.
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2020-07-15 12:38:43 -07:00
|
|
|
#[derive(Clone, Debug)]
|
2021-03-01 07:29:07 -08:00
|
|
|
enum Inner<S: SpendAuth, B: Binding<Scalar = S::Scalar, Point = S::Point>> {
|
2020-07-03 15:23:28 -07:00
|
|
|
SpendAuth {
|
2021-03-01 07:29:07 -08:00
|
|
|
vk_bytes: VerificationKeyBytes<S>,
|
|
|
|
sig: Signature<S>,
|
|
|
|
c: S::Scalar,
|
2020-07-03 15:23:28 -07:00
|
|
|
},
|
|
|
|
Binding {
|
2021-03-01 07:29:07 -08:00
|
|
|
vk_bytes: VerificationKeyBytes<B>,
|
|
|
|
sig: Signature<B>,
|
|
|
|
c: B::Scalar,
|
2020-07-03 15:23:28 -07:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A batch verification item.
|
|
|
|
///
|
|
|
|
/// This struct exists to allow batch processing to be decoupled from the
|
|
|
|
/// lifetime of the message. This is useful when using the batch verification API
|
|
|
|
/// in an async context.
|
2020-07-15 12:38:43 -07:00
|
|
|
#[derive(Clone, Debug)]
|
2021-03-01 07:29:07 -08:00
|
|
|
pub struct Item<S: SpendAuth, B: Binding<Scalar = S::Scalar, Point = S::Point>> {
|
|
|
|
inner: Inner<S, B>,
|
2020-07-03 15:23:28 -07:00
|
|
|
}
|
|
|
|
|
2021-03-01 07:29:07 -08:00
|
|
|
impl<S: SpendAuth, B: Binding<Scalar = S::Scalar, Point = S::Point>> Item<S, B> {
|
|
|
|
/// Create a batch item from a `SpendAuth` signature.
|
|
|
|
pub fn from_spendauth<'msg, M: AsRef<[u8]>>(
|
|
|
|
vk_bytes: VerificationKeyBytes<S>,
|
|
|
|
sig: Signature<S>,
|
|
|
|
msg: &'msg M,
|
2020-07-03 15:23:28 -07:00
|
|
|
) -> Self {
|
|
|
|
// Compute c now to avoid dependency on the msg lifetime.
|
2021-03-01 07:29:07 -08:00
|
|
|
let c = HStar::<S>::default()
|
2020-07-03 15:23:28 -07:00
|
|
|
.update(&sig.r_bytes[..])
|
|
|
|
.update(&vk_bytes.bytes[..])
|
|
|
|
.update(msg)
|
|
|
|
.finalize();
|
|
|
|
Self {
|
|
|
|
inner: Inner::SpendAuth { vk_bytes, sig, c },
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-01 07:29:07 -08:00
|
|
|
/// Create a batch item from a `Binding` signature.
|
|
|
|
pub fn from_binding<'msg, M: AsRef<[u8]>>(
|
|
|
|
vk_bytes: VerificationKeyBytes<B>,
|
|
|
|
sig: Signature<B>,
|
|
|
|
msg: &'msg M,
|
2020-07-03 15:23:28 -07:00
|
|
|
) -> Self {
|
|
|
|
// Compute c now to avoid dependency on the msg lifetime.
|
2021-03-01 07:29:07 -08:00
|
|
|
let c = HStar::<B>::default()
|
2020-07-03 15:23:28 -07:00
|
|
|
.update(&sig.r_bytes[..])
|
|
|
|
.update(&vk_bytes.bytes[..])
|
|
|
|
.update(msg)
|
|
|
|
.finalize();
|
|
|
|
Self {
|
|
|
|
inner: Inner::Binding { vk_bytes, sig, c },
|
2020-07-15 12:38:43 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Perform non-batched verification of this `Item`.
|
|
|
|
///
|
|
|
|
/// This is useful (in combination with `Item::clone`) for implementing fallback
|
|
|
|
/// logic when batch verification fails. In contrast to
|
|
|
|
/// [`VerificationKey::verify`](crate::VerificationKey::verify), which requires
|
|
|
|
/// borrowing the message data, the `Item` type is unlinked from the lifetime of
|
|
|
|
/// the message.
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
pub fn verify_single(self) -> Result<(), Error> {
|
|
|
|
match self.inner {
|
2021-03-01 07:29:07 -08:00
|
|
|
Inner::Binding { vk_bytes, sig, c } => {
|
|
|
|
VerificationKey::<B>::try_from(vk_bytes).and_then(|vk| vk.verify_prehashed(&sig, c))
|
|
|
|
}
|
2020-07-15 12:38:43 -07:00
|
|
|
Inner::SpendAuth { vk_bytes, sig, c } => {
|
2021-03-01 07:29:07 -08:00
|
|
|
VerificationKey::<S>::try_from(vk_bytes).and_then(|vk| vk.verify_prehashed(&sig, c))
|
2020-07-15 12:38:43 -07:00
|
|
|
}
|
2020-07-03 15:23:28 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A batch verification context.
|
2021-03-01 07:29:07 -08:00
|
|
|
pub struct Verifier<S: SpendAuth, B: Binding<Scalar = S::Scalar, Point = S::Point>> {
|
2020-07-03 15:23:28 -07:00
|
|
|
/// Signature data queued for verification.
|
2021-03-01 07:29:07 -08:00
|
|
|
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![] }
|
|
|
|
}
|
2020-07-03 15:23:28 -07:00
|
|
|
}
|
|
|
|
|
2021-03-01 07:29:07 -08:00
|
|
|
impl<S: SpendAuth, B: Binding<Scalar = S::Scalar, Point = S::Point>> Verifier<S, B> {
|
2020-07-03 15:23:28 -07:00
|
|
|
/// Construct a new batch verifier.
|
2021-03-01 07:29:07 -08:00
|
|
|
pub fn new() -> Verifier<S, B> {
|
2020-07-03 15:23:28 -07:00
|
|
|
Verifier::default()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Queue an Item for verification.
|
2021-03-01 07:29:07 -08:00
|
|
|
pub fn queue<I: Into<Item<S, B>>>(&mut self, item: I) {
|
2020-07-03 15:23:28 -07:00
|
|
|
self.signatures.push(item.into());
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Perform batch verification, returning `Ok(())` if all signatures were
|
|
|
|
/// valid and `Err` otherwise.
|
|
|
|
///
|
|
|
|
/// The batch verification equation is:
|
|
|
|
///
|
2021-02-25 08:06:54 -08:00
|
|
|
/// h_G * -[sum(z_i * s_i)]P_G + sum(\[z_i\]R_i + [z_i * c_i]VK_i) = 0_G
|
2020-07-03 15:23:28 -07:00
|
|
|
///
|
|
|
|
/// which we split out into:
|
|
|
|
///
|
2021-02-25 08:06:54 -08:00
|
|
|
/// h_G * -[sum(z_i * s_i)]P_G + sum(\[z_i\]R_i) + sum([z_i * c_i]VK_i) = 0_G
|
2020-07-03 15:23:28 -07:00
|
|
|
///
|
|
|
|
/// so that we can use multiscalar multiplication speedups.
|
|
|
|
///
|
|
|
|
/// where for each signature i,
|
|
|
|
/// - VK_i is the verification key;
|
|
|
|
/// - R_i is the signature's R value;
|
|
|
|
/// - s_i is the signature's s value;
|
|
|
|
/// - c_i is the hash of the message and other data;
|
|
|
|
/// - z_i is a random 128-bit Scalar;
|
|
|
|
/// - h_G is the cofactor of the group;
|
|
|
|
/// - P_G is the generator of the subgroup;
|
|
|
|
///
|
2021-03-01 07:29:07 -08:00
|
|
|
/// Since RedDSA uses different subgroups for different types
|
2020-07-03 15:23:28 -07:00
|
|
|
/// of signatures, SpendAuth's and Binding's, we need to have yet
|
|
|
|
/// another point and associated scalar accumulator for all the
|
|
|
|
/// signatures of each type in our batch, but we can still
|
|
|
|
/// amortize computation nicely in one multiscalar multiplication:
|
|
|
|
///
|
2021-02-25 08:06:54 -08:00
|
|
|
/// h_G * ( [-sum(z_i * s_i): i_type == SpendAuth]P_SpendAuth + [-sum(z_i * s_i): i_type == Binding]P_Binding + sum(\[z_i\]R_i) + sum([z_i * c_i]VK_i) ) = 0_G
|
2020-07-03 15:23:28 -07:00
|
|
|
///
|
|
|
|
/// As follows elliptic curve scalar multiplication convention,
|
|
|
|
/// scalar variables are lowercase and group point variables
|
|
|
|
/// are uppercase. This does not exactly match the RedDSA
|
|
|
|
/// notation in the [protocol specification §B.1][ps].
|
|
|
|
///
|
|
|
|
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#reddsabatchverify
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
pub fn verify<R: RngCore + CryptoRng>(self, mut rng: R) -> Result<(), Error> {
|
2022-09-12 12:47:24 -07:00
|
|
|
// https://p.z.cash/TCR:bad-txns-orchard-binding-signature-invalid?partial
|
2020-07-03 15:23:28 -07:00
|
|
|
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());
|
2021-03-01 07:29:07 -08:00
|
|
|
let mut P_spendauth_coeff = S::Scalar::zero();
|
|
|
|
let mut P_binding_coeff = B::Scalar::zero();
|
2020-07-03 15:23:28 -07:00
|
|
|
|
|
|
|
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 = {
|
|
|
|
// XXX-jubjub: should not use CtOption here
|
2021-03-01 07:29:07 -08:00
|
|
|
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);
|
2020-07-03 15:23:28 -07:00
|
|
|
if maybe_scalar.is_some().into() {
|
|
|
|
maybe_scalar.unwrap()
|
|
|
|
} else {
|
|
|
|
return Err(Error::InvalidSignature);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let R = {
|
|
|
|
// XXX-jubjub: should not use CtOption here
|
|
|
|
// XXX-jubjub: inconsistent ownership in from_bytes
|
2021-03-01 07:29:07 -08:00
|
|
|
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);
|
2020-07-03 15:23:28 -07:00
|
|
|
if maybe_point.is_some().into() {
|
2021-03-01 07:29:07 -08:00
|
|
|
maybe_point.unwrap()
|
2020-07-03 15:23:28 -07:00
|
|
|
} else {
|
|
|
|
return Err(Error::InvalidSignature);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let VK = match item.inner {
|
|
|
|
Inner::SpendAuth { vk_bytes, .. } => {
|
2021-03-01 07:29:07 -08:00
|
|
|
VerificationKey::<S>::try_from(vk_bytes.bytes)?.point
|
2020-07-03 15:23:28 -07:00
|
|
|
}
|
|
|
|
Inner::Binding { vk_bytes, .. } => {
|
2021-03-01 07:29:07 -08:00
|
|
|
VerificationKey::<B>::try_from(vk_bytes.bytes)?.point
|
2020-07-03 15:23:28 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-03-01 07:29:07 -08:00
|
|
|
let z = S::Scalar::from_raw(gen_128_bits(&mut rng));
|
2020-07-03 15:23:28 -07:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2021-03-01 07:29:07 -08:00
|
|
|
VK_coeffs.push(S::Scalar::zero() + (z * c));
|
2020-07-03 15:23:28 -07:00
|
|
|
VKs.push(VK);
|
|
|
|
}
|
|
|
|
|
2022-05-06 07:57:45 -07:00
|
|
|
use core::iter::once;
|
2020-07-03 15:23:28 -07:00
|
|
|
|
|
|
|
let scalars = once(&P_spendauth_coeff)
|
|
|
|
.chain(once(&P_binding_coeff))
|
|
|
|
.chain(VK_coeffs.iter())
|
|
|
|
.chain(R_coeffs.iter());
|
|
|
|
|
2021-03-01 07:29:07 -08:00
|
|
|
let basepoints = [S::basepoint(), B::basepoint()];
|
2020-07-03 15:23:28 -07:00
|
|
|
let points = basepoints.iter().chain(VKs.iter()).chain(Rs.iter());
|
|
|
|
|
2021-03-01 07:29:07 -08:00
|
|
|
let check = S::Point::vartime_multiscalar_mul(scalars, points);
|
2020-07-03 15:23:28 -07:00
|
|
|
|
|
|
|
if check.is_small_order().into() {
|
|
|
|
Ok(())
|
|
|
|
} else {
|
|
|
|
Err(Error::InvalidSignature)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|