1use core::{
2 borrow::Borrow,
3 fmt,
4 iter::Sum,
5 ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign},
6};
7
8use blst::*;
9use ff::Field;
10use group::Group;
11use rand_core::RngCore;
12use subtle::{Choice, ConstantTimeEq};
13
14use crate::{Scalar, fp::Fp, fp2::Fp2, fp6::Fp6, fp12::Fp12, traits::Compress};
15
16#[derive(Copy, Clone, Debug, Default)]
22#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
23#[repr(transparent)]
24pub struct Gt(pub(crate) Fp12);
25
26impl fmt::Display for Gt {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28 write!(f, "{:?}", self)
29 }
30}
31
32impl From<Fp12> for Gt {
33 fn from(fp12: Fp12) -> Self {
34 Gt(fp12)
35 }
36}
37
38impl From<Gt> for Fp12 {
39 fn from(gt: Gt) -> Self {
40 gt.0
41 }
42}
43
44impl Eq for Gt {}
45
46impl PartialEq for Gt {
47 #[inline]
48 fn eq(&self, other: &Self) -> bool {
49 self.0 == other.0
50 }
51}
52
53impl Neg for &Gt {
54 type Output = Gt;
55
56 #[inline]
57 fn neg(self) -> Gt {
58 let mut res = *self;
60 res.0.conjugate();
61 res
62 }
63}
64
65impl Neg for Gt {
66 type Output = Gt;
67
68 #[inline]
69 fn neg(self) -> Gt {
70 -&self
71 }
72}
73
74impl Add<&Gt> for &Gt {
75 type Output = Gt;
76
77 #[inline]
78 #[allow(clippy::suspicious_arithmetic_impl)]
79 fn add(self, rhs: &Gt) -> Gt {
80 Gt(self.0 * rhs.0)
81 }
82}
83
84impl Sub<&Gt> for &Gt {
85 type Output = Gt;
86
87 #[inline]
88 fn sub(self, rhs: &Gt) -> Gt {
89 self + (-rhs)
90 }
91}
92
93impl Mul<&Scalar> for &Gt {
94 type Output = Gt;
95
96 #[allow(clippy::suspicious_arithmetic_impl)]
97 fn mul(self, scalar: &Scalar) -> Self::Output {
98 let mut acc = Gt::identity();
99
100 for bit in scalar
107 .to_bytes_be()
108 .iter()
109 .flat_map(|byte| (0..8).rev().map(move |i| (byte >> i) & 1 == 1))
110 .skip(1)
111 {
112 acc = acc.double();
113 if bit {
114 acc += self;
115 }
116 }
117
118 acc
119 }
120}
121
122impl AddAssign<&Gt> for Gt {
123 #[inline]
124 fn add_assign(&mut self, rhs: &Gt) {
125 *self = *self + rhs;
126 }
127}
128
129impl SubAssign<&Gt> for Gt {
130 #[inline]
131 fn sub_assign(&mut self, rhs: &Gt) {
132 *self = *self - rhs;
133 }
134}
135
136impl MulAssign<&Scalar> for Gt {
137 #[inline]
138 fn mul_assign(&mut self, rhs: &Scalar) {
139 *self = *self * rhs;
140 }
141}
142
143impl_add_sub!(Gt);
144impl_add_sub_assign!(Gt);
145impl_mul!(Gt, Scalar);
146impl_mul_assign!(Gt, Scalar);
147
148impl<T> Sum<T> for Gt
149where
150 T: Borrow<Gt>,
151{
152 fn sum<I>(iter: I) -> Self
153 where
154 I: Iterator<Item = T>,
155 {
156 iter.fold(Self::identity(), |acc, item| acc + item.borrow())
157 }
158}
159
160impl Group for Gt {
161 type Scalar = Scalar;
162
163 fn random(mut rng: impl RngCore) -> Self {
164 loop {
165 let mut out = Fp12::random(&mut rng);
166
167 if !bool::from(out.is_zero()) {
171 unsafe { blst_final_exp(&mut out.0, &out.0) };
172 return Gt(out);
173 }
174 }
175 }
176
177 fn identity() -> Self {
179 Gt(Fp12::ONE)
180 }
181
182 fn generator() -> Self {
183 Gt(Fp12::new(
185 Fp6::new(
186 Fp2::new(
187 Fp::from_raw_unchecked([
188 0x1972_e433_a01f_85c5,
189 0x97d3_2b76_fd77_2538,
190 0xc8ce_546f_c96b_cdf9,
191 0xcef6_3e73_66d4_0614,
192 0xa611_3427_8184_3780,
193 0x13f3_448a_3fc6_d825,
194 ]),
195 Fp::from_raw_unchecked([
196 0xd263_31b0_2e9d_6995,
197 0x9d68_a482_f779_7e7d,
198 0x9c9b_2924_8d39_ea92,
199 0xf480_1ca2_e131_07aa,
200 0xa16c_0732_bdbc_b066,
201 0x083c_a4af_ba36_0478,
202 ]),
203 ),
204 Fp2::new(
205 Fp::from_raw_unchecked([
206 0x59e2_61db_0916_b641,
207 0x2716_b6f4_b23e_960d,
208 0xc8e5_5b10_a0bd_9c45,
209 0x0bdb_0bd9_9c4d_eda8,
210 0x8cf8_9ebf_57fd_aac5,
211 0x12d6_b792_9e77_7a5e,
212 ]),
213 Fp::from_raw_unchecked([
214 0x5fc8_5188_b0e1_5f35,
215 0x34a0_6e3a_8f09_6365,
216 0xdb31_26a6_e02a_d62c,
217 0xfc6f_5aa9_7d9a_990b,
218 0xa12f_55f5_eb89_c210,
219 0x1723_703a_926f_8889,
220 ]),
221 ),
222 Fp2::new(
223 Fp::from_raw_unchecked([
224 0x9358_8f29_7182_8778,
225 0x43f6_5b86_11ab_7585,
226 0x3183_aaf5_ec27_9fdf,
227 0xfa73_d7e1_8ac9_9df6,
228 0x64e1_76a6_a64c_99b0,
229 0x179f_a78c_5838_8f1f,
230 ]),
231 Fp::from_raw_unchecked([
232 0x672a_0a11_ca2a_ef12,
233 0x0d11_b9b5_2aa3_f16b,
234 0xa444_12d0_699d_056e,
235 0xc01d_0177_221a_5ba5,
236 0x66e0_cede_6c73_5529,
237 0x05f5_a71e_9fdd_c339,
238 ]),
239 ),
240 ),
241 Fp6::new(
242 Fp2::new(
243 Fp::from_raw_unchecked([
244 0xd30a_88a1_b062_c679,
245 0x5ac5_6a5d_35fc_8304,
246 0xd0c8_34a6_a81f_290d,
247 0xcd54_30c2_da37_07c7,
248 0xf0c2_7ff7_8050_0af0,
249 0x0924_5da6_e2d7_2eae,
250 ]),
251 Fp::from_raw_unchecked([
252 0x9f2e_0676_791b_5156,
253 0xe2d1_c823_4918_fe13,
254 0x4c9e_459f_3c56_1bf4,
255 0xa3e8_5e53_b9d3_e3c1,
256 0x820a_121e_21a7_0020,
257 0x15af_6183_41c5_9acc,
258 ]),
259 ),
260 Fp2::new(
261 Fp::from_raw_unchecked([
262 0x7c95_658c_2499_3ab1,
263 0x73eb_3872_1ca8_86b9,
264 0x5256_d749_4774_34bc,
265 0x8ba4_1902_ea50_4a8b,
266 0x04a3_d3f8_0c86_ce6d,
267 0x18a6_4a87_fb68_6eaa,
268 ]),
269 Fp::from_raw_unchecked([
270 0xbb83_e71b_b920_cf26,
271 0x2a52_77ac_92a7_3945,
272 0xfc0e_e59f_94f0_46a0,
273 0x7158_cdf3_7860_58f7,
274 0x7cc1_061b_82f9_45f6,
275 0x03f8_47aa_9fdb_e567,
276 ]),
277 ),
278 Fp2::new(
279 Fp::from_raw_unchecked([
280 0x8078_dba5_6134_e657,
281 0x1cd7_ec9a_4399_8a6e,
282 0xb1aa_599a_1a99_3766,
283 0xc9a0_f62f_0842_ee44,
284 0x8e15_9be3_b605_dffa,
285 0x0c86_ba0d_4af1_3fc2,
286 ]),
287 Fp::from_raw_unchecked([
288 0xe80f_f2a0_6a52_ffb1,
289 0x7694_ca48_721a_906c,
290 0x7583_183e_03b0_8514,
291 0xf567_afdd_40ce_e4e2,
292 0x9a6d_96d2_e526_a5fc,
293 0x197e_9f49_861f_2242,
294 ]),
295 ),
296 ),
297 ))
298 }
299
300 fn is_identity(&self) -> Choice {
301 self.0.ct_eq(&Self::identity().0)
302 }
303
304 fn double(&self) -> Self {
305 Gt(self.0.square())
306 }
307}
308
309#[derive(Copy, Clone, PartialEq, Eq, Debug)]
311#[repr(transparent)]
312pub struct GtCompressed(pub(crate) Fp6);
313
314impl Gt {
315 pub fn compress(&self) -> Option<GtCompressed> {
317 let mut c0 = self.0.c0();
320
321 c0.0.fp2[0] = (c0.c0() + Fp2::from(1)).0;
322 let b = c0 * self.0.c1().invert().unwrap();
323
324 Some(GtCompressed(b))
325 }
326
327 fn is_in_subgroup(&self) -> bool {
328 unsafe { blst_fp12_in_group(&(self.0).0) }
329 }
330}
331
332impl GtCompressed {
333 pub fn uncompress(self) -> Option<Gt> {
336 let fp6_neg_one = Fp6::from(1).neg();
340 let t = Fp12::new(self.0, fp6_neg_one).invert().unwrap();
341 let c = Fp12::new(self.0, Fp6::from(1)) * t;
342 let g = Gt(c);
343
344 if g.is_in_subgroup() {
345 return Some(g);
346 }
347
348 None
349 }
350}
351
352impl Compress for Gt {
353 fn write_compressed<W: std::io::Write>(self, mut out: W) -> std::io::Result<()> {
354 let c = self.compress().unwrap();
355
356 out.write_all(&c.0.c0().c0().to_bytes_le())?;
357 out.write_all(&c.0.c0().c1().to_bytes_le())?;
358
359 out.write_all(&c.0.c1().c0().to_bytes_le())?;
360 out.write_all(&c.0.c1().c1().to_bytes_le())?;
361
362 out.write_all(&c.0.c2().c0().to_bytes_le())?;
363 out.write_all(&c.0.c2().c1().to_bytes_le())?;
364
365 Ok(())
366 }
367
368 fn read_compressed<R: std::io::Read>(mut source: R) -> std::io::Result<Self> {
369 let mut buffer = [0u8; 48];
370 let read_fp = |source: &mut dyn std::io::Read, buffer: &mut [u8; 48]| {
371 source.read_exact(buffer)?;
372 let fp = Fp::from_bytes_le(buffer);
373 Option::from(fp)
374 .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid fp"))
375 };
376
377 let x0 = read_fp(&mut source, &mut buffer)?;
378 let x1 = read_fp(&mut source, &mut buffer)?;
379
380 let y0 = read_fp(&mut source, &mut buffer)?;
381 let y1 = read_fp(&mut source, &mut buffer)?;
382
383 let z0 = read_fp(&mut source, &mut buffer)?;
384 let z1 = read_fp(&mut source, &mut buffer)?;
385
386 let x = Fp2::new(x0, x1);
387 let y = Fp2::new(y0, y1);
388 let z = Fp2::new(z0, z1);
389
390 let compressed = GtCompressed(Fp6::new(x, y, z));
391 compressed.uncompress().ok_or_else(|| {
392 std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid compression point")
393 })
394 }
395}
396
397#[cfg(test)]
398mod tests {
399 use super::*;
400
401 use group::{Curve, prime::PrimeCurveAffine};
402 use pairing_lib::{Engine, MillerLoopResult, MultiMillerLoop};
403 use rand_core::SeedableRng;
404 use rand_xorshift::XorShiftRng;
405
406 use crate::{Bls12, G1Affine, G1Projective, G2Affine, G2Prepared, G2Projective, pairing};
407
408 #[test]
409 fn test_gt_generator() {
410 assert_eq!(
411 Gt::generator(),
412 pairing(&G1Affine::generator(), &G2Affine::generator()),
413 );
414 }
415
416 #[test]
417 fn test_gt_bilinearity() {
418 use crate::Scalar;
419
420 let a = Scalar::from_u64s_le(&[1, 2, 3, 4])
421 .unwrap()
422 .invert()
423 .unwrap()
424 .square();
425 let b = Scalar::from_u64s_le(&[5, 6, 7, 8])
426 .unwrap()
427 .invert()
428 .unwrap()
429 .square();
430 let c = a * b;
431
432 let g = G1Affine::from(G1Affine::generator() * a);
433 let h = G2Affine::from(G2Affine::generator() * b);
434 let p = pairing(&g, &h);
435
436 assert_ne!(p, Gt::identity());
437 assert_eq!(
438 p,
439 pairing(
440 &G1Affine::from(G1Affine::generator() * c),
441 &G2Affine::generator()
442 ),
443 );
444 assert_eq!(
445 p,
446 pairing(&G1Affine::generator(), &G2Affine::generator()) * c
447 );
448 }
449
450 #[test]
451 fn test_gt_unitary() {
452 let g = G1Affine::generator();
453 let h = G2Affine::generator();
454 let p = -pairing(&g, &h);
455 let q = pairing(&g, &-h);
456 let r = pairing(&-g, &h);
457
458 assert_eq!(p, q);
459 assert_eq!(q, r);
460 }
461
462 #[test]
463 fn test_multi_miller_loop() {
464 let a1 = G1Affine::generator();
465 let b1 = G2Affine::generator();
466
467 let a2 = G1Affine::from(
468 G1Affine::generator()
469 * Scalar::from_u64s_le(&[1, 2, 3, 4])
470 .unwrap()
471 .invert()
472 .unwrap()
473 .square(),
474 );
475 let b2 = G2Affine::from(
476 G2Affine::generator()
477 * Scalar::from_u64s_le(&[4, 2, 2, 4])
478 .unwrap()
479 .invert()
480 .unwrap()
481 .square(),
482 );
483
484 let a3 = G1Affine::identity();
485 let b3 = G2Affine::from(
486 G2Affine::generator()
487 * Scalar::from_u64s_le(&[9, 2, 2, 4])
488 .unwrap()
489 .invert()
490 .unwrap()
491 .square(),
492 );
493
494 let a4 = G1Affine::from(
495 G1Affine::generator()
496 * Scalar::from_u64s_le(&[5, 5, 5, 5])
497 .unwrap()
498 .invert()
499 .unwrap()
500 .square(),
501 );
502 let b4 = G2Affine::identity();
503
504 let a5 = G1Affine::from(
505 G1Affine::generator()
506 * Scalar::from_u64s_le(&[323, 32, 3, 1])
507 .unwrap()
508 .invert()
509 .unwrap()
510 .square(),
511 );
512 let b5 = G2Affine::from(
513 G2Affine::generator()
514 * Scalar::from_u64s_le(&[4, 2, 2, 9099])
515 .unwrap()
516 .invert()
517 .unwrap()
518 .square(),
519 );
520
521 let b1_prepared = G2Prepared::from(b1);
522 let b2_prepared = G2Prepared::from(b2);
523 let b3_prepared = G2Prepared::from(b3);
524 let b4_prepared = G2Prepared::from(b4);
525 let b5_prepared = G2Prepared::from(b5);
526
527 let expected = Bls12::pairing(&a1, &b1)
528 + Bls12::pairing(&a2, &b2)
529 + Bls12::pairing(&a3, &b3)
530 + Bls12::pairing(&a4, &b4)
531 + Bls12::pairing(&a5, &b5);
532
533 let test = <Bls12 as MultiMillerLoop>::multi_miller_loop(&[
534 (&a1, &b1_prepared),
535 (&a2, &b2_prepared),
536 (&a3, &b3_prepared),
537 (&a4, &b4_prepared),
538 (&a5, &b5_prepared),
539 ])
540 .final_exponentiation();
541
542 assert_eq!(expected, test);
543 }
544
545 #[test]
546 fn gt_compression() {
547 let mut rng = XorShiftRng::from_seed([
548 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
549 0xbc, 0xe5,
550 ]);
551
552 for i in 0..100 {
553 let a = Gt::random(&mut rng);
554 if let Some(b) = a.compress() {
556 let c = b.uncompress().unwrap();
557 assert_eq!(a, c, "{}", i);
558 } else {
559 println!("skipping {}", i);
560 }
561
562 let p = G1Projective::random(&mut rng).to_affine();
564 let q = G2Projective::random(&mut rng).to_affine();
565 let a: Gt = crate::pairing(&p, &q);
566 assert!(a.is_in_subgroup());
567
568 let b = a.compress().unwrap();
569 let c = b.uncompress().unwrap();
570 assert_eq!(a, c, "{}", i);
571
572 let mut buffer = Vec::new();
573 a.write_compressed(&mut buffer).unwrap();
574 let out = Gt::read_compressed(std::io::Cursor::new(buffer)).unwrap();
575 assert_eq!(a, out);
576 }
577 }
578
579 #[test]
580 fn gt_subgroup() {
581 let mut rng = XorShiftRng::from_seed([
582 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
583 0xbc, 0xe5,
584 ]);
585 let p = G1Projective::random(&mut rng).to_affine();
586 let q = G2Projective::random(&mut rng).to_affine();
587 let a = crate::pairing(&p, &q);
588 assert!(a.is_in_subgroup());
589 }
590}