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}