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}