ed25519_consensus/batch.rs
1//! Performs batch Ed25519 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 of
7//! caller code (which must assemble a batch of signatures across work-items),
8//! and loss of the ability to easily pinpoint failing signatures.
9//!
10//! In addition to these general tradeoffs, design flaws in Ed25519 specifically
11//! mean that batched verification may not agree with individual verification.
12//! Some signatures may verify as part of a batch but not on their own.
13//! This problem is fixed by [ZIP215], a precise specification for edge cases
14//! in Ed25519 signature validation that ensures that batch verification agrees
15//! with individual verification in all cases.
16//!
17//! This crate implements ZIP215, so batch verification always agrees with
18//! individual verification, but this is not guaranteed by other implementations.
19//! **Be extremely careful when using Ed25519 in a consensus-critical context
20//! like a blockchain.**
21//!
22//! This batch verification implementation is adaptive in the sense that it
23//! detects multiple signatures created with the same verification key and
24//! automatically coalesces terms in the final verification equation. In the
25//! limiting case where all signatures in the batch are made with the same
26//! verification key, coalesced batch verification runs twice as fast as ordinary
27//! batch verification.
28//!
29//! 
30//!
31//! This optimization doesn't help much when public keys are random,
32//! but could be useful in proof-of-stake systems where signatures come from a
33//! set of validators (provided that system uses the ZIP215 rules).
34//!
35//! # Example
36//! ```
37//! # use ed25519_consensus::*;
38//! let mut batch = batch::Verifier::new();
39//! for _ in 0..32 {
40//! let sk = SigningKey::new(rand::thread_rng());
41//! let vk_bytes = VerificationKeyBytes::from(&sk);
42//! let msg = b"BatchVerifyTest";
43//! let sig = sk.sign(&msg[..]);
44//! batch.queue((vk_bytes, sig, &msg[..]));
45//! }
46//! assert!(batch.verify(rand::thread_rng()).is_ok());
47//! ```
48//!
49//! [ZIP215]: https://github.com/zcash/zips/blob/master/zip-0215.rst
50
51use std::{collections::HashMap, convert::TryFrom};
52
53use curve25519_dalek::{
54 edwards::{CompressedEdwardsY, EdwardsPoint},
55 scalar::Scalar,
56 traits::{IsIdentity, VartimeMultiscalarMul},
57};
58use rand_core::{CryptoRng, RngCore};
59use sha2::{Digest, Sha512};
60
61use crate::{Error, Signature, VerificationKey, VerificationKeyBytes};
62
63// Shim to generate a u128 without importing `rand`.
64fn gen_u128<R: RngCore + CryptoRng>(mut rng: R) -> u128 {
65 let mut bytes = [0u8; 16];
66 rng.fill_bytes(&mut bytes[..]);
67 u128::from_le_bytes(bytes)
68}
69
70/// A batch verification item.
71///
72/// This struct exists to allow batch processing to be decoupled from the
73/// lifetime of the message. This is useful when using the batch verification API
74/// in an async context.
75#[derive(Clone, Debug)]
76pub struct Item {
77 vk_bytes: VerificationKeyBytes,
78 sig: Signature,
79 k: Scalar,
80}
81
82impl<'msg, M: AsRef<[u8]> + ?Sized> From<(VerificationKeyBytes, Signature, &'msg M)> for Item {
83 fn from(tup: (VerificationKeyBytes, Signature, &'msg M)) -> Self {
84 let (vk_bytes, sig, msg) = tup;
85 // Compute k now to avoid dependency on the msg lifetime.
86 let k = Scalar::from_hash(
87 Sha512::default()
88 .chain(&sig.R_bytes[..])
89 .chain(&vk_bytes.0[..])
90 .chain(msg),
91 );
92 Self { vk_bytes, sig, k }
93 }
94}
95
96impl Item {
97 /// Perform non-batched verification of this `Item`.
98 ///
99 /// This is useful (in combination with `Item::clone`) for implementing fallback
100 /// logic when batch verification fails. In contrast to
101 /// [`VerificationKey::verify`](crate::VerificationKey::verify), which requires
102 /// borrowing the message data, the `Item` type is unlinked from the lifetime of
103 /// the message.
104 pub fn verify_single(self) -> Result<(), Error> {
105 VerificationKey::try_from(self.vk_bytes)
106 .and_then(|vk| vk.verify_prehashed(&self.sig, self.k))
107 }
108}
109
110/// A batch verification context.
111#[derive(Default)]
112pub struct Verifier {
113 /// Signature data queued for verification.
114 signatures: HashMap<VerificationKeyBytes, Vec<(Scalar, Signature)>>,
115 /// Caching this count avoids a hash traversal to figure out
116 /// how much to preallocate.
117 batch_size: usize,
118}
119
120impl Verifier {
121 /// Construct a new batch verifier.
122 pub fn new() -> Verifier {
123 Verifier::default()
124 }
125
126 /// Queue a (key, signature, message) tuple for verification.
127 pub fn queue<I: Into<Item>>(&mut self, item: I) {
128 let Item { vk_bytes, sig, k } = item.into();
129
130 self.signatures
131 .entry(vk_bytes)
132 // The common case is 1 signature per public key.
133 // We could also consider using a smallvec here.
134 .or_insert_with(|| Vec::with_capacity(1))
135 .push((k, sig));
136 self.batch_size += 1;
137 }
138
139 /// Perform batch verification, returning `Ok(())` if all signatures were
140 /// valid and `Err` otherwise.
141 ///
142 /// # Warning
143 ///
144 /// Ed25519 has different verification rules for batched and non-batched
145 /// verifications. This function does not have the same verification criteria
146 /// as individual verification, which may reject some signatures this method
147 /// accepts.
148 #[allow(non_snake_case)]
149 pub fn verify<R: RngCore + CryptoRng>(self, mut rng: R) -> Result<(), Error> {
150 // The batch verification equation is
151 //
152 // [-sum(z_i * s_i)]B + sum([z_i]R_i) + sum([z_i * k_i]A_i) = 0.
153 //
154 // where for each signature i,
155 // - A_i is the verification key;
156 // - R_i is the signature's R value;
157 // - s_i is the signature's s value;
158 // - k_i is the hash of the message and other data;
159 // - z_i is a random 128-bit Scalar.
160 //
161 // Normally n signatures would require a multiscalar multiplication of
162 // size 2*n + 1, together with 2*n point decompressions (to obtain A_i
163 // and R_i). However, because we store batch entries in a HashMap
164 // indexed by the verification key, we can "coalesce" all z_i * k_i
165 // terms for each distinct verification key into a single coefficient.
166 //
167 // For n signatures from m verification keys, this approach instead
168 // requires a multiscalar multiplication of size n + m + 1 together with
169 // n + m point decompressions. When m = n, so all signatures are from
170 // distinct verification keys, this is as efficient as the usual method.
171 // However, when m = 1 and all signatures are from a single verification
172 // key, this is nearly twice as fast.
173
174 let m = self.signatures.keys().count();
175
176 let mut A_coeffs = Vec::with_capacity(m);
177 let mut As = Vec::with_capacity(m);
178 let mut R_coeffs = Vec::with_capacity(self.batch_size);
179 let mut Rs = Vec::with_capacity(self.batch_size);
180 let mut B_coeff = Scalar::zero();
181
182 for (vk_bytes, sigs) in self.signatures.iter() {
183 let A = CompressedEdwardsY(vk_bytes.0)
184 .decompress()
185 .ok_or(Error::InvalidSignature)?;
186
187 let mut A_coeff = Scalar::zero();
188
189 for (k, sig) in sigs.iter() {
190 let R = CompressedEdwardsY(sig.R_bytes)
191 .decompress()
192 .ok_or(Error::InvalidSignature)?;
193 let s = Scalar::from_canonical_bytes(sig.s_bytes).ok_or(Error::InvalidSignature)?;
194 let z = Scalar::from(gen_u128(&mut rng));
195 B_coeff -= z * s;
196 Rs.push(R);
197 R_coeffs.push(z);
198 A_coeff += z * k;
199 }
200
201 As.push(A);
202 A_coeffs.push(A_coeff);
203 }
204
205 use core::iter::once;
206 use curve25519_dalek::constants::ED25519_BASEPOINT_POINT as B;
207 let check = EdwardsPoint::vartime_multiscalar_mul(
208 once(&B_coeff).chain(A_coeffs.iter()).chain(R_coeffs.iter()),
209 once(&B).chain(As.iter()).chain(Rs.iter()),
210 );
211
212 if check.mul_by_cofactor().is_identity() {
213 Ok(())
214 } else {
215 Err(Error::InvalidSignature)
216 }
217 }
218}