frost_core/
batch.rs

1//! Performs batch Schnorr signature verification.
2//!
3//! Batch verification asks whether *all* signatures in some set are valid,
4//! rather than asking whether *each* of them is valid. This allows sharing
5//! computations among all signature verifications, performing less work overall
6//! at the cost of higher latency (the entire batch must complete), complexity
7//! of caller code (which must assemble a batch of signatures across
8//! work-items), and loss of the ability to easily pinpoint failing signatures.
9
10use rand_core::{CryptoRng, RngCore};
11
12use crate::{scalar_mul::VartimeMultiscalarMul, Ciphersuite, Element, *};
13
14/// A batch verification item.
15///
16/// This struct exists to allow batch processing to be decoupled from the
17/// lifetime of the message. This is useful when using the batch verification
18/// API in an async context.
19#[derive(Clone, Debug)]
20pub struct Item<C: Ciphersuite> {
21    vk: VerifyingKey<C>,
22    sig: Signature<C>,
23    c: Challenge<C>,
24}
25
26impl<C> Item<C>
27where
28    C: Ciphersuite,
29{
30    /// Create a new batch [`Item`] from a [`VerifyingKey`], [`Signature`]
31    /// and a message to be verified.
32    pub fn new<M>(vk: VerifyingKey<C>, sig: Signature<C>, msg: M) -> Result<Self, Error<C>>
33    where
34        M: AsRef<[u8]>,
35    {
36        let (msg, sig, vk) = <C>::pre_verify(msg.as_ref(), &sig, &vk)?;
37        let c = <C>::challenge(&sig.R, &vk, &msg)?;
38
39        Ok(Self {
40            vk: *vk,
41            sig: *sig,
42            c,
43        })
44    }
45}
46
47impl<C> Item<C>
48where
49    C: Ciphersuite,
50{
51    /// Perform non-batched verification of this `Item`.
52    ///
53    /// This is useful (in combination with `Item::clone`) for implementing
54    /// fallback logic when batch verification fails. In contrast to
55    /// [`VerifyingKey::verify`](crate::VerifyingKey::verify), which
56    /// requires borrowing the message data, the `Item` type is unlinked
57    /// from the lifetime of the message.
58    pub fn verify_single(self) -> Result<(), Error<C>> {
59        self.vk.verify_prehashed(self.c, &self.sig)
60    }
61}
62
63/// A batch verification context.
64pub struct Verifier<C: Ciphersuite> {
65    /// Signature data queued for verification.
66    signatures: Vec<Item<C>>,
67}
68
69impl<C> Verifier<C>
70where
71    C: Ciphersuite,
72{
73    /// Constructs a new batch verifier.
74    pub fn new() -> Verifier<C> {
75        Verifier::default()
76    }
77
78    /// Queues an Item for verification.
79    pub fn queue<I: Into<Item<C>>>(&mut self, item: I) {
80        self.signatures.push(item.into());
81    }
82
83    /// Performs batch verification, returning `Ok(())` if all signatures were
84    /// valid and `Err` otherwise, or if the batch is empty.
85    ///
86    /// The batch verification equation is:
87    ///
88    /// h_G * -[sum(z_i * s_i)]P_G + sum(\[z_i\]R_i + [z_i * c_i]VK_i) = 0_G
89    ///
90    /// which we split out into:
91    ///
92    /// h_G * -[sum(z_i * s_i)]P_G + sum(\[z_i\]R_i) + sum([z_i * c_i]VK_i) =
93    /// 0_G
94    ///
95    /// so that we can use multiscalar multiplication speedups.
96    ///
97    /// where for each signature i,
98    /// - VK_i is the verification key;
99    /// - R_i is the signature's R value;
100    /// - s_i is the signature's s value;
101    /// - c_i is the hash of the message and other data;
102    /// - z_i is a random 128-bit Scalar;
103    /// - h_G is the cofactor of the group;
104    /// - P_G is the generator of the subgroup;
105    ///
106    /// As follows elliptic curve scalar multiplication convention,
107    /// scalar variables are lowercase and group point variables
108    /// are uppercase. This does not exactly match the RedDSA
109    /// notation in the [protocol specification §B.1][ps].
110    ///
111    /// [ps]: https://zips.z.cash/protocol/protocol.pdf#reddsabatchverify
112    pub fn verify<R: RngCore + CryptoRng>(self, mut rng: R) -> Result<(), Error<C>> {
113        let n = self.signatures.len();
114
115        if n == 0 {
116            return Err(Error::InvalidSignature);
117        }
118
119        let mut VK_coeffs = Vec::with_capacity(n);
120        let mut VKs = Vec::with_capacity(n);
121        let mut R_coeffs = Vec::with_capacity(n);
122        let mut Rs = Vec::with_capacity(n);
123        let mut P_coeff_acc = <<C::Group as Group>::Field>::zero();
124
125        for item in self.signatures.iter() {
126            let z = item.sig.z;
127            let R = item.sig.R;
128
129            let blind = <<C::Group as Group>::Field>::random(&mut rng);
130
131            let P_coeff = blind * z;
132            P_coeff_acc = P_coeff_acc - P_coeff;
133
134            R_coeffs.push(blind);
135            Rs.push(R);
136
137            VK_coeffs.push(<<C::Group as Group>::Field>::zero() + (blind * item.c.0));
138            VKs.push(item.vk.to_element());
139        }
140
141        let scalars = core::iter::once(&P_coeff_acc)
142            .chain(VK_coeffs.iter())
143            .chain(R_coeffs.iter());
144
145        let basepoints = [C::Group::generator()];
146        let points = basepoints.iter().chain(VKs.iter()).chain(Rs.iter());
147
148        let check: Element<C> =
149            VartimeMultiscalarMul::<C>::vartime_multiscalar_mul(scalars, points);
150
151        if (check * <C::Group>::cofactor()) == <C::Group>::identity() {
152            Ok(())
153        } else {
154            Err(Error::InvalidSignature)
155        }
156    }
157}
158
159impl<C> Default for Verifier<C>
160where
161    C: Ciphersuite,
162{
163    fn default() -> Self {
164        Self {
165            signatures: Vec::new(),
166        }
167    }
168}