commonware_cryptography/bls12381/primitives/
variant.rs

1//! Different variants of the BLS signature scheme.
2
3use super::{
4    group::{
5        Point, DST, G1, G1_MESSAGE, G1_PROOF_OF_POSSESSION, G2, G2_MESSAGE, G2_PROOF_OF_POSSESSION,
6        GT,
7    },
8    Error,
9};
10use crate::bls12381::primitives::group::{Element, Scalar};
11#[cfg(not(feature = "std"))]
12use alloc::vec::Vec;
13use blst::{
14    blst_final_exp, blst_fp12, blst_miller_loop, Pairing as blst_pairing, BLS12_381_NEG_G1,
15    BLS12_381_NEG_G2,
16};
17use commonware_codec::FixedSize;
18use core::{
19    fmt::{Debug, Formatter},
20    hash::Hash,
21};
22use rand_core::CryptoRngCore;
23
24/// A specific instance of a signature scheme.
25pub trait Variant: Clone + Send + Sync + Hash + Eq + Debug + 'static {
26    /// The public key type.
27    type Public: Point + FixedSize + Debug + Hash + Copy + AsRef<Self::Public>;
28
29    /// The signature type.
30    type Signature: Point + FixedSize + Debug + Hash + Copy + AsRef<Self::Signature>;
31
32    /// The domain separator tag (DST) for a proof of possession.
33    const PROOF_OF_POSSESSION: DST;
34
35    /// The domain separator tag (DST) for a message.
36    const MESSAGE: DST;
37
38    /// Verify the signature from the provided public key and pre-hashed message.
39    fn verify(
40        public: &Self::Public,
41        hm: &Self::Signature,
42        signature: &Self::Signature,
43    ) -> Result<(), Error>;
44
45    /// Verify a batch of signatures from the provided public keys and pre-hashed messages.
46    fn batch_verify<R: CryptoRngCore>(
47        rng: &mut R,
48        publics: &[Self::Public],
49        hms: &[Self::Signature],
50        signatures: &[Self::Signature],
51    ) -> Result<(), Error>;
52
53    /// Compute the pairing `e(G1, G2) -> GT`.
54    fn pairing(public: &Self::Public, signature: &Self::Signature) -> GT;
55}
56
57/// A [Variant] with a public key of type [G1] and a signature of type [G2].
58#[derive(Clone, Hash, PartialEq, Eq)]
59pub struct MinPk {}
60
61impl Variant for MinPk {
62    type Public = G1;
63    type Signature = G2;
64
65    const PROOF_OF_POSSESSION: DST = G2_PROOF_OF_POSSESSION;
66    const MESSAGE: DST = G2_MESSAGE;
67
68    /// Verifies that `e(hm,pk)` is equal to `e(sig,G1::one())` using a single product check with
69    /// a negated G1 generator (`e(hm,pk) * e(sig,-G1::one()) == 1`).
70    fn verify(
71        public: &Self::Public,
72        hm: &Self::Signature,
73        signature: &Self::Signature,
74    ) -> Result<(), Error> {
75        // Create a pairing context
76        //
77        // We only handle pre-hashed messages, so we leave the domain separator tag (`DST`) empty.
78        let mut pairing = blst_pairing::new(false, &[]);
79
80        // Convert `sig` into affine and aggregate `e(sig,-G1::one())`
81        let q = signature.as_blst_p2_affine();
82        unsafe {
83            pairing.raw_aggregate(&q, &BLS12_381_NEG_G1);
84        }
85
86        // Convert `pk` and `hm` into affine
87        let p = public.as_blst_p1_affine();
88        let q = hm.as_blst_p2_affine();
89
90        // Aggregate `e(hm,pk)`
91        pairing.raw_aggregate(&q, &p);
92
93        // Finalize the pairing accumulation and verify the result
94        //
95        // If `finalverify()` returns `true`, it means `e(hm,pk) * e(sig,-G1::one()) == 1`. This
96        // is equivalent to `e(hm,pk) == e(sig,G1::one())`.
97        pairing.commit();
98        if !pairing.finalverify(None) {
99            return Err(Error::InvalidSignature);
100        }
101        Ok(())
102    }
103
104    /// Verifies a set of signatures against their respective public keys and pre-hashed messages.
105    ///
106    /// This method is outperforms individual signature verification (`2` pairings per signature) by
107    /// verifying a random linear combination of the public keys and signatures (`n+1` pairings and
108    /// `2n` multiplications for `n` signatures).
109    ///
110    /// The verification equation for each signature `i` is:
111    /// `e(hm_i,pk_i) == e(sig_i,G1::one())`,
112    /// which is equivalent to checking if `e(hm_i,pk_i) * e(sig_i,-G1::one()) == 1`.
113    ///
114    /// To batch verify `n` such equations, we introduce random non-zero scalars `r_i` (for `i=1..n`).
115    /// The batch verification checks if the product of these individual equations, each raised to the power
116    /// of its respective `r_i`, equals one:
117    /// `prod_i((e(hm_i,pk_i) * e(sig_i,-G1::one()))^{r_i}) == 1`
118    ///
119    /// Using the bilinearity of pairings, this can be rewritten (by moving `r_i` inside the pairings):
120    /// `prod_i(e(hm_i,r_i * pk_i) * e(r_i * sig_i,-G1::one())) == 1`
121    ///
122    /// The second term `e(r_i * sig_i,-G1::one())` can be computed efficiently with Multi-Scalar Multiplication:
123    /// `e(sum_i(r_i * sig_i),-G1::one())`
124    ///
125    /// Finally, we aggregate all pairings `e(hm_i,r_i * pk_i)` (`n`) and `e(sum_i(r_i * sig_i),-G1::one())` (`1`)
126    /// into a single product in the target group `G_T`. If the result is the identity element in `G_T`,
127    /// the batch verification succeeds.
128    ///
129    /// Source: <https://ethresear.ch/t/security-of-bls-batch-verification/10748>
130    fn batch_verify<R: CryptoRngCore>(
131        rng: &mut R,
132        publics: &[Self::Public],
133        hms: &[Self::Signature],
134        signatures: &[Self::Signature],
135    ) -> Result<(), Error> {
136        // Ensure there is an equal number of public keys, messages, and signatures.
137        assert_eq!(publics.len(), hms.len());
138        assert_eq!(publics.len(), signatures.len());
139        if publics.is_empty() {
140            return Ok(());
141        }
142
143        // Generate random non-zero scalars.
144        let scalars: Vec<Scalar> = (0..publics.len())
145            .map(|_| loop {
146                let scalar = Scalar::from_rand(rng);
147                if scalar != Scalar::zero() {
148                    return scalar;
149                }
150            })
151            .collect();
152
153        // Compute S_agg = sum(r_i * sig_i) using Multi-Scalar Multiplication (MSM).
154        let s_agg = G2::msm(signatures, &scalars);
155
156        // Initialize pairing context. DST is empty as we use pre-hashed messages.
157        let mut pairing = blst_pairing::new(false, &[]);
158
159        // Aggregate the single term corresponding to signatures: e(-G1::one(),S_agg)
160        let s_agg_affine = s_agg.as_blst_p2_affine();
161        unsafe {
162            pairing.raw_aggregate(&s_agg_affine, &BLS12_381_NEG_G1);
163        }
164
165        // Aggregate the `n` terms corresponding to public keys and messages: e(r_i * pk_i,hm_i)
166        for i in 0..publics.len() {
167            let mut scaled_pk = publics[i];
168            scaled_pk.mul(&scalars[i]);
169            let pk_affine = scaled_pk.as_blst_p1_affine();
170            let hm_affine = hms[i].as_blst_p2_affine();
171            pairing.raw_aggregate(&hm_affine, &pk_affine);
172        }
173
174        // Perform the final verification on the product of (n+1) pairing terms.
175        pairing.commit();
176        if !pairing.finalverify(None) {
177            return Err(Error::InvalidSignature);
178        }
179        Ok(())
180    }
181
182    /// Compute the pairing `e(public, signature) -> GT`.
183    fn pairing(public: &Self::Public, signature: &Self::Signature) -> GT {
184        let p1_affine = public.as_blst_p1_affine();
185        let p2_affine = signature.as_blst_p2_affine();
186
187        let mut result = blst_fp12::default();
188        unsafe {
189            blst_miller_loop(&mut result, &p2_affine, &p1_affine);
190            blst_final_exp(&mut result, &result);
191        }
192
193        GT::from_blst_fp12(result)
194    }
195}
196
197impl Debug for MinPk {
198    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
199        f.debug_struct("MinPk").finish()
200    }
201}
202
203/// A [Variant] with a public key of type [G2] and a signature of type [G1].
204#[derive(Clone, Hash, PartialEq, Eq)]
205pub struct MinSig {}
206
207impl Variant for MinSig {
208    type Public = G2;
209    type Signature = G1;
210
211    const PROOF_OF_POSSESSION: DST = G1_PROOF_OF_POSSESSION;
212    const MESSAGE: DST = G1_MESSAGE;
213
214    /// Verifies that `e(pk,hm)` is equal to `e(G2::one(),sig)` using a single product check with
215    /// a negated G2 generator (`e(pk,hm) * e(-G2::one(),sig) == 1`).
216    fn verify(
217        public: &Self::Public,
218        hm: &Self::Signature,
219        signature: &Self::Signature,
220    ) -> Result<(), Error> {
221        // Create a pairing context
222        //
223        // We only handle pre-hashed messages, so we leave the domain separator tag (`DST`) empty.
224        let mut pairing = blst_pairing::new(false, &[]);
225
226        // Convert `sig` into affine and aggregate `e(-G2::one(), sig)`
227        let q = signature.as_blst_p1_affine();
228        unsafe {
229            pairing.raw_aggregate(&BLS12_381_NEG_G2, &q);
230        }
231
232        // Convert `pk` and `hm` into affine
233        let p = public.as_blst_p2_affine();
234        let q = hm.as_blst_p1_affine();
235
236        // Aggregate `e(pk,hm)`
237        pairing.raw_aggregate(&p, &q);
238
239        // Finalize the pairing accumulation and verify the result
240        //
241        // If `finalverify()` returns `true`, it means `e(pk,hm) * e(-G2::one(),sig) == 1`. This
242        // is equivalent to `e(pk,hm) == e(G2::one(),sig)`.
243        pairing.commit();
244        if !pairing.finalverify(None) {
245            return Err(Error::InvalidSignature);
246        }
247        Ok(())
248    }
249
250    /// Verifies a set of signatures against their respective public keys and pre-hashed messages.
251    ///
252    /// This method outperforms individual signature verification (`2` pairings per signature) by
253    /// verifying a random linear combination of the public keys and signatures (`n+1` pairings and
254    /// `2n` multiplications for `n` signatures).
255    ///
256    /// The verification equation for each signature `i` is:
257    /// `e(pk_i,hm_i) == e(G2::one(),sig_i)`,
258    /// which is equivalent to checking if `e(pk_i,hm_i) * e(-G2::one(),sig_i) == 1`.
259    ///
260    /// To batch verify `n` such equations, we introduce random non-zero scalars `r_i` (for `i=1..n`).
261    /// The batch verification checks if the product of these individual equations, each effectively
262    /// raised to the power of its respective `r_i`, equals one:
263    /// `prod_i((e(pk_i,hm_i) * e(-G2::one(),sig_i))^{r_i}) == 1`
264    ///
265    /// Using the bilinearity of pairings, this can be rewritten (by moving `r_i` inside the pairings):
266    /// `prod_i(e(r_i * pk_i,hm_i) * e(-G2::one(),r_i * sig_i)) == 1`
267    ///
268    /// The second term `e(-G2::one(),r_i * sig_i)` can be computed efficiently with Multi-Scalar Multiplication:
269    /// `e(-G2::one(),sum_i(r_i * sig_i))`
270    ///
271    /// Finally, we aggregate all pairings `e(r_i * pk_i,hm_i)` (`n`) and `e(-G2::one(),sum_i(r_i * sig_i))` (`1`)
272    /// into a single product in the target group `G_T`. If the result is the identity element in `G_T`,
273    /// the batch verification succeeds.
274    ///
275    /// Source: <https://ethresear.ch/t/security-of-bls-batch-verification/10748>
276    fn batch_verify<R: CryptoRngCore>(
277        rng: &mut R,
278        publics: &[Self::Public],
279        hms: &[Self::Signature],
280        signatures: &[Self::Signature],
281    ) -> Result<(), Error> {
282        // Ensure there is an equal number of public keys, messages, and signatures.
283        assert_eq!(publics.len(), hms.len());
284        assert_eq!(publics.len(), signatures.len());
285        if publics.is_empty() {
286            return Ok(());
287        }
288
289        // Generate random non-zero scalars.
290        let scalars: Vec<Scalar> = (0..publics.len())
291            .map(|_| loop {
292                let scalar = Scalar::from_rand(rng);
293                if scalar != Scalar::zero() {
294                    return scalar;
295                }
296            })
297            .collect();
298
299        // Compute S_agg = sum(r_i * sig_i) using Multi-Scalar Multiplication (MSM).
300        let s_agg = G1::msm(signatures, &scalars);
301
302        // Initialize pairing context. DST is empty as we use pre-hashed messages.
303        let mut pairing = blst_pairing::new(false, &[]);
304
305        // Aggregate the single term corresponding to signatures: e(S_agg,-G2::one())
306        let s_agg_affine = s_agg.as_blst_p1_affine();
307        unsafe {
308            pairing.raw_aggregate(&BLS12_381_NEG_G2, &s_agg_affine);
309        }
310
311        // Aggregate the `n` terms corresponding to public keys and messages: e(hm_i, r_i * pk_i)
312        for i in 0..publics.len() {
313            let mut scaled_pk = publics[i];
314            scaled_pk.mul(&scalars[i]);
315            let pk_affine = scaled_pk.as_blst_p2_affine();
316            let hm_affine = hms[i].as_blst_p1_affine();
317            pairing.raw_aggregate(&pk_affine, &hm_affine);
318        }
319
320        // Perform the final verification on the product of (n+1) pairing terms.
321        pairing.commit();
322        if !pairing.finalverify(None) {
323            return Err(Error::InvalidSignature);
324        }
325        Ok(())
326    }
327
328    /// Compute the pairing `e(signature, public) -> GT`.
329    fn pairing(public: &Self::Public, signature: &Self::Signature) -> GT {
330        let p1_affine = signature.as_blst_p1_affine();
331        let p2_affine = public.as_blst_p2_affine();
332
333        let mut result = blst_fp12::default();
334        unsafe {
335            blst_miller_loop(&mut result, &p2_affine, &p1_affine);
336            blst_final_exp(&mut result, &result);
337        }
338
339        GT::from_blst_fp12(result)
340    }
341}
342
343impl Debug for MinSig {
344    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
345        f.debug_struct("MinSig").finish()
346    }
347}