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