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