decaf377_rdsa/
batch.rs

1//! Performs batch `decaf377-rdsa` 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
11use std::convert::TryFrom;
12
13use decaf377::{Element, Fr};
14use rand_core::{CryptoRng, RngCore};
15
16use crate::{
17    domain::Sealed, Binding, Error, HStar, Signature, SpendAuth, VerificationKey,
18    VerificationKeyBytes,
19};
20
21// Shim to generate a random 128bit Fr value.
22fn gen_128_bits<R: RngCore + CryptoRng>(mut rng: R) -> Fr {
23    let lo = rng.next_u64() as u128;
24    let hi = rng.next_u64() as u128;
25    (lo + (hi << 64)).into()
26}
27
28#[derive(Clone, Debug)]
29enum Inner {
30    SpendAuth {
31        vk_bytes: VerificationKeyBytes<SpendAuth>,
32        sig: Signature<SpendAuth>,
33        c: Fr,
34    },
35    Binding {
36        vk_bytes: VerificationKeyBytes<Binding>,
37        sig: Signature<Binding>,
38        c: Fr,
39    },
40}
41
42/// A batch verification item.
43///
44/// This struct exists to allow batch processing to be decoupled from the
45/// lifetime of the message. This is useful when using the batch verification API
46/// in an async context.
47#[derive(Clone, Debug)]
48pub struct Item {
49    inner: Inner,
50}
51
52impl<'msg, M: AsRef<[u8]>>
53    From<(
54        VerificationKeyBytes<SpendAuth>,
55        Signature<SpendAuth>,
56        &'msg M,
57    )> for Item
58{
59    fn from(
60        (vk_bytes, sig, msg): (
61            VerificationKeyBytes<SpendAuth>,
62            Signature<SpendAuth>,
63            &'msg M,
64        ),
65    ) -> Self {
66        // Compute c now to avoid dependency on the msg lifetime.
67        let c = HStar::default()
68            .update(&sig.r_bytes()[..])
69            .update(&vk_bytes.bytes[..])
70            .update(msg)
71            .finalize();
72        Self {
73            inner: Inner::SpendAuth { vk_bytes, sig, c },
74        }
75    }
76}
77
78impl<'msg, M: AsRef<[u8]>> From<(VerificationKeyBytes<Binding>, Signature<Binding>, &'msg M)>
79    for Item
80{
81    fn from(
82        (vk_bytes, sig, msg): (VerificationKeyBytes<Binding>, Signature<Binding>, &'msg M),
83    ) -> Self {
84        // Compute c now to avoid dependency on the msg lifetime.
85        let c = HStar::default()
86            .update(&sig.r_bytes()[..])
87            .update(&vk_bytes.bytes[..])
88            .update(msg)
89            .finalize();
90        Self {
91            inner: Inner::Binding { vk_bytes, sig, c },
92        }
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    #[allow(non_snake_case)]
105    pub fn verify_single(self) -> Result<(), Error> {
106        match self.inner {
107            Inner::Binding { vk_bytes, sig, c } => VerificationKey::<Binding>::try_from(vk_bytes)
108                .and_then(|vk| vk.verify_prehashed(&sig, c)),
109            Inner::SpendAuth { vk_bytes, sig, c } => {
110                VerificationKey::<SpendAuth>::try_from(vk_bytes)
111                    .and_then(|vk| vk.verify_prehashed(&sig, c))
112            }
113        }
114    }
115}
116
117#[derive(Default)]
118/// A batch verification context.
119pub struct Verifier {
120    /// Signature data queued for verification.
121    signatures: Vec<Item>,
122}
123
124impl Verifier {
125    /// Construct a new batch verifier.
126    pub fn new() -> Verifier {
127        Verifier::default()
128    }
129
130    /// Queue an Item for verification.
131    pub fn queue<I: Into<Item>>(&mut self, item: I) {
132        self.signatures.push(item.into());
133    }
134
135    /// Perform batch verification, returning `Ok(())` if all signatures were
136    /// valid and `Err` otherwise.
137    ///
138    /// The batch verification equation is:
139    ///
140    /// ```ascii
141    /// h_G * -[sum(z_i * s_i)]P_G + sum([z_i]R_i + [z_i * c_i]VK_i) = 0_G
142    /// ```
143    ///
144    /// which we split out into:
145    ///
146    /// ```ascii
147    /// h_G * -[sum(z_i * s_i)]P_G + sum([z_i]R_i) + sum([z_i * c_i]VK_i) = 0_G
148    /// ```
149    ///
150    /// so that we can use multiscalar multiplication speedups.
151    ///
152    /// where for each signature i,
153    /// - VK_i is the verification key;
154    /// - R_i is the signature's R value;
155    /// - s_i is the signature's s value;
156    /// - c_i is the hash of the message and other data;
157    /// - z_i is a random 128-bit Scalar;
158    /// - h_G is the cofactor of the group;
159    /// - P_G is the generator of the subgroup;
160    ///
161    /// Since `decaf377-rdsa` uses a different generator for each signature
162    /// domain, we have a separate scalar accumulator for each domain, but we
163    /// can still amortize computation nicely in one multiscalar multiplication:
164    ///
165    /// ```ascii
166    /// 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
167    /// ```
168    ///
169    /// As follows elliptic curve scalar multiplication convention,
170    /// scalar variables are lowercase and group point variables
171    /// are uppercase. This does not exactly match the RedDSA
172    /// notation in the [Zcash protocol specification §B.1][ps].
173    ///
174    /// [ps]: https://zips.z.cash/protocol/protocol.pdf#reddsabatchverify
175    #[allow(non_snake_case)]
176    pub fn verify<R: RngCore + CryptoRng>(self, mut rng: R) -> Result<(), Error> {
177        let n = self.signatures.len();
178
179        let mut VK_coeffs = Vec::with_capacity(n);
180        let mut VKs = Vec::with_capacity(n);
181        let mut R_coeffs = Vec::with_capacity(self.signatures.len());
182        let mut Rs = Vec::with_capacity(self.signatures.len());
183        let mut P_spendauth_coeff = Fr::ZERO;
184        let mut P_binding_coeff = Fr::ZERO;
185
186        for item in self.signatures.iter() {
187            let (s_bytes, r_bytes, c) = match item.inner {
188                Inner::SpendAuth { sig, c, .. } => (sig.s_bytes(), sig.r_bytes(), c),
189                Inner::Binding { sig, c, .. } => (sig.s_bytes(), sig.r_bytes(), c),
190            };
191
192            let s = Fr::from_bytes_checked(&s_bytes).map_err(|_| Error::InvalidSignature)?;
193            let R = decaf377::Encoding(r_bytes)
194                .vartime_decompress()
195                .map_err(|_| Error::InvalidSignature)?;
196
197            let VK = match item.inner {
198                Inner::SpendAuth { vk_bytes, .. } => {
199                    VerificationKey::<SpendAuth>::try_from(vk_bytes.bytes)?.point
200                }
201                Inner::Binding { vk_bytes, .. } => {
202                    VerificationKey::<Binding>::try_from(vk_bytes.bytes)?.point
203                }
204            };
205
206            let z = gen_128_bits(&mut rng);
207
208            let P_coeff = z * s;
209            match item.inner {
210                Inner::SpendAuth { .. } => {
211                    P_spendauth_coeff -= P_coeff;
212                }
213                Inner::Binding { .. } => {
214                    P_binding_coeff -= P_coeff;
215                }
216            };
217
218            R_coeffs.push(z);
219            Rs.push(R);
220
221            VK_coeffs.push(z * c);
222            VKs.push(VK);
223        }
224
225        use std::iter::once;
226
227        let scalars = once(&P_spendauth_coeff)
228            .chain(once(&P_binding_coeff))
229            .chain(VK_coeffs.iter())
230            .chain(R_coeffs.iter());
231
232        let basepoints = [SpendAuth::basepoint(), Binding::basepoint()];
233        let points = basepoints.iter().chain(VKs.iter()).chain(Rs.iter());
234
235        let check = Element::vartime_multiscalar_mul(scalars, points);
236
237        if check.is_identity() {
238            Ok(())
239        } else {
240            Err(Error::InvalidSignature)
241        }
242    }
243}