bls12_381/
fp2.rs

1//! This module implements arithmetic over the quadratic extension field Fp2.
2
3use core::fmt;
4use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
5use rand_core::RngCore;
6use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
7
8use crate::fp::Fp;
9
10#[derive(Copy, Clone)]
11pub struct Fp2 {
12    pub c0: Fp,
13    pub c1: Fp,
14}
15
16impl fmt::Debug for Fp2 {
17    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
18        write!(f, "{:?} + {:?}*u", self.c0, self.c1)
19    }
20}
21
22impl Default for Fp2 {
23    fn default() -> Self {
24        Fp2::zero()
25    }
26}
27
28#[cfg(feature = "zeroize")]
29impl zeroize::DefaultIsZeroes for Fp2 {}
30
31impl From<Fp> for Fp2 {
32    fn from(f: Fp) -> Fp2 {
33        Fp2 {
34            c0: f,
35            c1: Fp::zero(),
36        }
37    }
38}
39
40impl ConstantTimeEq for Fp2 {
41    fn ct_eq(&self, other: &Self) -> Choice {
42        self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1)
43    }
44}
45
46impl Eq for Fp2 {}
47impl PartialEq for Fp2 {
48    #[inline]
49    fn eq(&self, other: &Self) -> bool {
50        bool::from(self.ct_eq(other))
51    }
52}
53
54impl ConditionallySelectable for Fp2 {
55    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
56        Fp2 {
57            c0: Fp::conditional_select(&a.c0, &b.c0, choice),
58            c1: Fp::conditional_select(&a.c1, &b.c1, choice),
59        }
60    }
61}
62
63impl<'a> Neg for &'a Fp2 {
64    type Output = Fp2;
65
66    #[inline]
67    fn neg(self) -> Fp2 {
68        self.neg()
69    }
70}
71
72impl Neg for Fp2 {
73    type Output = Fp2;
74
75    #[inline]
76    fn neg(self) -> Fp2 {
77        -&self
78    }
79}
80
81impl<'a, 'b> Sub<&'b Fp2> for &'a Fp2 {
82    type Output = Fp2;
83
84    #[inline]
85    fn sub(self, rhs: &'b Fp2) -> Fp2 {
86        self.sub(rhs)
87    }
88}
89
90impl<'a, 'b> Add<&'b Fp2> for &'a Fp2 {
91    type Output = Fp2;
92
93    #[inline]
94    fn add(self, rhs: &'b Fp2) -> Fp2 {
95        self.add(rhs)
96    }
97}
98
99impl<'a, 'b> Mul<&'b Fp2> for &'a Fp2 {
100    type Output = Fp2;
101
102    #[inline]
103    fn mul(self, rhs: &'b Fp2) -> Fp2 {
104        self.mul(rhs)
105    }
106}
107
108impl_binops_additive!(Fp2, Fp2);
109impl_binops_multiplicative!(Fp2, Fp2);
110
111impl Fp2 {
112    #[inline]
113    pub const fn zero() -> Fp2 {
114        Fp2 {
115            c0: Fp::zero(),
116            c1: Fp::zero(),
117        }
118    }
119
120    #[inline]
121    pub const fn one() -> Fp2 {
122        Fp2 {
123            c0: Fp::one(),
124            c1: Fp::zero(),
125        }
126    }
127
128    pub fn is_zero(&self) -> Choice {
129        self.c0.is_zero() & self.c1.is_zero()
130    }
131
132    pub(crate) fn random(mut rng: impl RngCore) -> Fp2 {
133        Fp2 {
134            c0: Fp::random(&mut rng),
135            c1: Fp::random(&mut rng),
136        }
137    }
138
139    /// Raises this element to p.
140    #[inline(always)]
141    pub fn frobenius_map(&self) -> Self {
142        // This is always just a conjugation. If you're curious why, here's
143        // an article about it: https://alicebob.cryptoland.net/the-frobenius-endomorphism-with-finite-fields/
144        self.conjugate()
145    }
146
147    #[inline(always)]
148    pub fn conjugate(&self) -> Self {
149        Fp2 {
150            c0: self.c0,
151            c1: -self.c1,
152        }
153    }
154
155    #[inline(always)]
156    pub fn mul_by_nonresidue(&self) -> Fp2 {
157        // Multiply a + bu by u + 1, getting
158        // au + a + bu^2 + bu
159        // and because u^2 = -1, we get
160        // (a - b) + (a + b)u
161
162        Fp2 {
163            c0: self.c0 - self.c1,
164            c1: self.c0 + self.c1,
165        }
166    }
167
168    /// Returns whether or not this element is strictly lexicographically
169    /// larger than its negation.
170    #[inline]
171    pub fn lexicographically_largest(&self) -> Choice {
172        // If this element's c1 coefficient is lexicographically largest
173        // then it is lexicographically largest. Otherwise, in the event
174        // the c1 coefficient is zero and the c0 coefficient is
175        // lexicographically largest, then this element is lexicographically
176        // largest.
177
178        self.c1.lexicographically_largest()
179            | (self.c1.is_zero() & self.c0.lexicographically_largest())
180    }
181
182    pub const fn square(&self) -> Fp2 {
183        // Complex squaring:
184        //
185        // v0  = c0 * c1
186        // c0' = (c0 + c1) * (c0 + \beta*c1) - v0 - \beta * v0
187        // c1' = 2 * v0
188        //
189        // In BLS12-381's F_{p^2}, our \beta is -1 so we
190        // can modify this formula:
191        //
192        // c0' = (c0 + c1) * (c0 - c1)
193        // c1' = 2 * c0 * c1
194
195        let a = (&self.c0).add(&self.c1);
196        let b = (&self.c0).sub(&self.c1);
197        let c = (&self.c0).add(&self.c0);
198
199        Fp2 {
200            c0: (&a).mul(&b),
201            c1: (&c).mul(&self.c1),
202        }
203    }
204
205    pub fn mul(&self, rhs: &Fp2) -> Fp2 {
206        // F_{p^2} x F_{p^2} multiplication implemented with operand scanning (schoolbook)
207        // computes the result as:
208        //
209        //   a·b = (a_0 b_0 + a_1 b_1 β) + (a_0 b_1 + a_1 b_0)i
210        //
211        // In BLS12-381's F_{p^2}, our β is -1, so the resulting F_{p^2} element is:
212        //
213        //   c_0 = a_0 b_0 - a_1 b_1
214        //   c_1 = a_0 b_1 + a_1 b_0
215        //
216        // Each of these is a "sum of products", which we can compute efficiently.
217
218        Fp2 {
219            c0: Fp::sum_of_products([self.c0, -self.c1], [rhs.c0, rhs.c1]),
220            c1: Fp::sum_of_products([self.c0, self.c1], [rhs.c1, rhs.c0]),
221        }
222    }
223
224    pub const fn add(&self, rhs: &Fp2) -> Fp2 {
225        Fp2 {
226            c0: (&self.c0).add(&rhs.c0),
227            c1: (&self.c1).add(&rhs.c1),
228        }
229    }
230
231    pub const fn sub(&self, rhs: &Fp2) -> Fp2 {
232        Fp2 {
233            c0: (&self.c0).sub(&rhs.c0),
234            c1: (&self.c1).sub(&rhs.c1),
235        }
236    }
237
238    pub const fn neg(&self) -> Fp2 {
239        Fp2 {
240            c0: (&self.c0).neg(),
241            c1: (&self.c1).neg(),
242        }
243    }
244
245    pub fn sqrt(&self) -> CtOption<Self> {
246        // Algorithm 9, https://eprint.iacr.org/2012/685.pdf
247        // with constant time modifications.
248
249        CtOption::new(Fp2::zero(), self.is_zero()).or_else(|| {
250            // a1 = self^((p - 3) / 4)
251            let a1 = self.pow_vartime(&[
252                0xee7f_bfff_ffff_eaaa,
253                0x07aa_ffff_ac54_ffff,
254                0xd9cc_34a8_3dac_3d89,
255                0xd91d_d2e1_3ce1_44af,
256                0x92c6_e9ed_90d2_eb35,
257                0x0680_447a_8e5f_f9a6,
258            ]);
259
260            // alpha = a1^2 * self = self^((p - 3) / 2 + 1) = self^((p - 1) / 2)
261            let alpha = a1.square() * self;
262
263            // x0 = self^((p + 1) / 4)
264            let x0 = a1 * self;
265
266            // In the event that alpha = -1, the element is order p - 1 and so
267            // we're just trying to get the square of an element of the subfield
268            // Fp. This is given by x0 * u, since u = sqrt(-1). Since the element
269            // x0 = a + bu has b = 0, the solution is therefore au.
270            CtOption::new(
271                Fp2 {
272                    c0: -x0.c1,
273                    c1: x0.c0,
274                },
275                alpha.ct_eq(&(&Fp2::one()).neg()),
276            )
277            // Otherwise, the correct solution is (1 + alpha)^((q - 1) // 2) * x0
278            .or_else(|| {
279                CtOption::new(
280                    (alpha + Fp2::one()).pow_vartime(&[
281                        0xdcff_7fff_ffff_d555,
282                        0x0f55_ffff_58a9_ffff,
283                        0xb398_6950_7b58_7b12,
284                        0xb23b_a5c2_79c2_895f,
285                        0x258d_d3db_21a5_d66b,
286                        0x0d00_88f5_1cbf_f34d,
287                    ]) * x0,
288                    Choice::from(1),
289                )
290            })
291            // Only return the result if it's really the square root (and so
292            // self is actually quadratic nonresidue)
293            .and_then(|sqrt| CtOption::new(sqrt, sqrt.square().ct_eq(self)))
294        })
295    }
296
297    /// Computes the multiplicative inverse of this field
298    /// element, returning None in the case that this element
299    /// is zero.
300    pub fn invert(&self) -> CtOption<Self> {
301        // We wish to find the multiplicative inverse of a nonzero
302        // element a + bu in Fp2. We leverage an identity
303        //
304        // (a + bu)(a - bu) = a^2 + b^2
305        //
306        // which holds because u^2 = -1. This can be rewritten as
307        //
308        // (a + bu)(a - bu)/(a^2 + b^2) = 1
309        //
310        // because a^2 + b^2 = 0 has no nonzero solutions for (a, b).
311        // This gives that (a - bu)/(a^2 + b^2) is the inverse
312        // of (a + bu). Importantly, this can be computing using
313        // only a single inversion in Fp.
314
315        (self.c0.square() + self.c1.square()).invert().map(|t| Fp2 {
316            c0: self.c0 * t,
317            c1: self.c1 * -t,
318        })
319    }
320
321    /// Although this is labeled "vartime", it is only
322    /// variable time with respect to the exponent. It
323    /// is also not exposed in the public API.
324    pub fn pow_vartime(&self, by: &[u64; 6]) -> Self {
325        let mut res = Self::one();
326        for e in by.iter().rev() {
327            for i in (0..64).rev() {
328                res = res.square();
329
330                if ((*e >> i) & 1) == 1 {
331                    res *= self;
332                }
333            }
334        }
335        res
336    }
337
338    /// Vartime exponentiation for larger exponents, only
339    /// used in testing and not exposed through the public API.
340    #[cfg(all(test, feature = "experimental"))]
341    pub(crate) fn pow_vartime_extended(&self, by: &[u64]) -> Self {
342        let mut res = Self::one();
343        for e in by.iter().rev() {
344            for i in (0..64).rev() {
345                res = res.square();
346
347                if ((*e >> i) & 1) == 1 {
348                    res *= self;
349                }
350            }
351        }
352        res
353    }
354}
355
356#[test]
357fn test_conditional_selection() {
358    let a = Fp2 {
359        c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]),
360        c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]),
361    };
362    let b = Fp2 {
363        c0: Fp::from_raw_unchecked([13, 14, 15, 16, 17, 18]),
364        c1: Fp::from_raw_unchecked([19, 20, 21, 22, 23, 24]),
365    };
366
367    assert_eq!(
368        ConditionallySelectable::conditional_select(&a, &b, Choice::from(0u8)),
369        a
370    );
371    assert_eq!(
372        ConditionallySelectable::conditional_select(&a, &b, Choice::from(1u8)),
373        b
374    );
375}
376
377#[test]
378fn test_equality() {
379    fn is_equal(a: &Fp2, b: &Fp2) -> bool {
380        let eq = a == b;
381        let ct_eq = a.ct_eq(&b);
382
383        assert_eq!(eq, bool::from(ct_eq));
384
385        eq
386    }
387
388    assert!(is_equal(
389        &Fp2 {
390            c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]),
391            c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]),
392        },
393        &Fp2 {
394            c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]),
395            c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]),
396        }
397    ));
398
399    assert!(!is_equal(
400        &Fp2 {
401            c0: Fp::from_raw_unchecked([2, 2, 3, 4, 5, 6]),
402            c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]),
403        },
404        &Fp2 {
405            c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]),
406            c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]),
407        }
408    ));
409
410    assert!(!is_equal(
411        &Fp2 {
412            c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]),
413            c1: Fp::from_raw_unchecked([2, 8, 9, 10, 11, 12]),
414        },
415        &Fp2 {
416            c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]),
417            c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]),
418        }
419    ));
420}
421
422#[test]
423fn test_squaring() {
424    let a = Fp2 {
425        c0: Fp::from_raw_unchecked([
426            0xc9a2_1831_63ee_70d4,
427            0xbc37_70a7_196b_5c91,
428            0xa247_f8c1_304c_5f44,
429            0xb01f_c2a3_726c_80b5,
430            0xe1d2_93e5_bbd9_19c9,
431            0x04b7_8e80_020e_f2ca,
432        ]),
433        c1: Fp::from_raw_unchecked([
434            0x952e_a446_0462_618f,
435            0x238d_5edd_f025_c62f,
436            0xf6c9_4b01_2ea9_2e72,
437            0x03ce_24ea_c1c9_3808,
438            0x0559_50f9_45da_483c,
439            0x010a_768d_0df4_eabc,
440        ]),
441    };
442    let b = Fp2 {
443        c0: Fp::from_raw_unchecked([
444            0xa1e0_9175_a4d2_c1fe,
445            0x8b33_acfc_204e_ff12,
446            0xe244_15a1_1b45_6e42,
447            0x61d9_96b1_b6ee_1936,
448            0x1164_dbe8_667c_853c,
449            0x0788_557a_cc7d_9c79,
450        ]),
451        c1: Fp::from_raw_unchecked([
452            0xda6a_87cc_6f48_fa36,
453            0x0fc7_b488_277c_1903,
454            0x9445_ac4a_dc44_8187,
455            0x0261_6d5b_c909_9209,
456            0xdbed_4677_2db5_8d48,
457            0x11b9_4d50_76c7_b7b1,
458        ]),
459    };
460
461    assert_eq!(a.square(), b);
462}
463
464#[test]
465fn test_multiplication() {
466    let a = Fp2 {
467        c0: Fp::from_raw_unchecked([
468            0xc9a2_1831_63ee_70d4,
469            0xbc37_70a7_196b_5c91,
470            0xa247_f8c1_304c_5f44,
471            0xb01f_c2a3_726c_80b5,
472            0xe1d2_93e5_bbd9_19c9,
473            0x04b7_8e80_020e_f2ca,
474        ]),
475        c1: Fp::from_raw_unchecked([
476            0x952e_a446_0462_618f,
477            0x238d_5edd_f025_c62f,
478            0xf6c9_4b01_2ea9_2e72,
479            0x03ce_24ea_c1c9_3808,
480            0x0559_50f9_45da_483c,
481            0x010a_768d_0df4_eabc,
482        ]),
483    };
484    let b = Fp2 {
485        c0: Fp::from_raw_unchecked([
486            0xa1e0_9175_a4d2_c1fe,
487            0x8b33_acfc_204e_ff12,
488            0xe244_15a1_1b45_6e42,
489            0x61d9_96b1_b6ee_1936,
490            0x1164_dbe8_667c_853c,
491            0x0788_557a_cc7d_9c79,
492        ]),
493        c1: Fp::from_raw_unchecked([
494            0xda6a_87cc_6f48_fa36,
495            0x0fc7_b488_277c_1903,
496            0x9445_ac4a_dc44_8187,
497            0x0261_6d5b_c909_9209,
498            0xdbed_4677_2db5_8d48,
499            0x11b9_4d50_76c7_b7b1,
500        ]),
501    };
502    let c = Fp2 {
503        c0: Fp::from_raw_unchecked([
504            0xf597_483e_27b4_e0f7,
505            0x610f_badf_811d_ae5f,
506            0x8432_af91_7714_327a,
507            0x6a9a_9603_cf88_f09e,
508            0xf05a_7bf8_bad0_eb01,
509            0x0954_9131_c003_ffae,
510        ]),
511        c1: Fp::from_raw_unchecked([
512            0x963b_02d0_f93d_37cd,
513            0xc95c_e1cd_b30a_73d4,
514            0x3087_25fa_3126_f9b8,
515            0x56da_3c16_7fab_0d50,
516            0x6b50_86b5_f4b6_d6af,
517            0x09c3_9f06_2f18_e9f2,
518        ]),
519    };
520
521    assert_eq!(a * b, c);
522}
523
524#[test]
525fn test_addition() {
526    let a = Fp2 {
527        c0: Fp::from_raw_unchecked([
528            0xc9a2_1831_63ee_70d4,
529            0xbc37_70a7_196b_5c91,
530            0xa247_f8c1_304c_5f44,
531            0xb01f_c2a3_726c_80b5,
532            0xe1d2_93e5_bbd9_19c9,
533            0x04b7_8e80_020e_f2ca,
534        ]),
535        c1: Fp::from_raw_unchecked([
536            0x952e_a446_0462_618f,
537            0x238d_5edd_f025_c62f,
538            0xf6c9_4b01_2ea9_2e72,
539            0x03ce_24ea_c1c9_3808,
540            0x0559_50f9_45da_483c,
541            0x010a_768d_0df4_eabc,
542        ]),
543    };
544    let b = Fp2 {
545        c0: Fp::from_raw_unchecked([
546            0xa1e0_9175_a4d2_c1fe,
547            0x8b33_acfc_204e_ff12,
548            0xe244_15a1_1b45_6e42,
549            0x61d9_96b1_b6ee_1936,
550            0x1164_dbe8_667c_853c,
551            0x0788_557a_cc7d_9c79,
552        ]),
553        c1: Fp::from_raw_unchecked([
554            0xda6a_87cc_6f48_fa36,
555            0x0fc7_b488_277c_1903,
556            0x9445_ac4a_dc44_8187,
557            0x0261_6d5b_c909_9209,
558            0xdbed_4677_2db5_8d48,
559            0x11b9_4d50_76c7_b7b1,
560        ]),
561    };
562    let c = Fp2 {
563        c0: Fp::from_raw_unchecked([
564            0x6b82_a9a7_08c1_32d2,
565            0x476b_1da3_39ba_5ba4,
566            0x848c_0e62_4b91_cd87,
567            0x11f9_5955_295a_99ec,
568            0xf337_6fce_2255_9f06,
569            0x0c3f_e3fa_ce8c_8f43,
570        ]),
571        c1: Fp::from_raw_unchecked([
572            0x6f99_2c12_73ab_5bc5,
573            0x3355_1366_17a1_df33,
574            0x8b0e_f74c_0aed_aff9,
575            0x062f_9246_8ad2_ca12,
576            0xe146_9770_738f_d584,
577            0x12c3_c3dd_84bc_a26d,
578        ]),
579    };
580
581    assert_eq!(a + b, c);
582}
583
584#[test]
585fn test_subtraction() {
586    let a = Fp2 {
587        c0: Fp::from_raw_unchecked([
588            0xc9a2_1831_63ee_70d4,
589            0xbc37_70a7_196b_5c91,
590            0xa247_f8c1_304c_5f44,
591            0xb01f_c2a3_726c_80b5,
592            0xe1d2_93e5_bbd9_19c9,
593            0x04b7_8e80_020e_f2ca,
594        ]),
595        c1: Fp::from_raw_unchecked([
596            0x952e_a446_0462_618f,
597            0x238d_5edd_f025_c62f,
598            0xf6c9_4b01_2ea9_2e72,
599            0x03ce_24ea_c1c9_3808,
600            0x0559_50f9_45da_483c,
601            0x010a_768d_0df4_eabc,
602        ]),
603    };
604    let b = Fp2 {
605        c0: Fp::from_raw_unchecked([
606            0xa1e0_9175_a4d2_c1fe,
607            0x8b33_acfc_204e_ff12,
608            0xe244_15a1_1b45_6e42,
609            0x61d9_96b1_b6ee_1936,
610            0x1164_dbe8_667c_853c,
611            0x0788_557a_cc7d_9c79,
612        ]),
613        c1: Fp::from_raw_unchecked([
614            0xda6a_87cc_6f48_fa36,
615            0x0fc7_b488_277c_1903,
616            0x9445_ac4a_dc44_8187,
617            0x0261_6d5b_c909_9209,
618            0xdbed_4677_2db5_8d48,
619            0x11b9_4d50_76c7_b7b1,
620        ]),
621    };
622    let c = Fp2 {
623        c0: Fp::from_raw_unchecked([
624            0xe1c0_86bb_bf1b_5981,
625            0x4faf_c3a9_aa70_5d7e,
626            0x2734_b5c1_0bb7_e726,
627            0xb2bd_7776_af03_7a3e,
628            0x1b89_5fb3_98a8_4164,
629            0x1730_4aef_6f11_3cec,
630        ]),
631        c1: Fp::from_raw_unchecked([
632            0x74c3_1c79_9519_1204,
633            0x3271_aa54_79fd_ad2b,
634            0xc9b4_7157_4915_a30f,
635            0x65e4_0313_ec44_b8be,
636            0x7487_b238_5b70_67cb,
637            0x0952_3b26_d0ad_19a4,
638        ]),
639    };
640
641    assert_eq!(a - b, c);
642}
643
644#[test]
645fn test_negation() {
646    let a = Fp2 {
647        c0: Fp::from_raw_unchecked([
648            0xc9a2_1831_63ee_70d4,
649            0xbc37_70a7_196b_5c91,
650            0xa247_f8c1_304c_5f44,
651            0xb01f_c2a3_726c_80b5,
652            0xe1d2_93e5_bbd9_19c9,
653            0x04b7_8e80_020e_f2ca,
654        ]),
655        c1: Fp::from_raw_unchecked([
656            0x952e_a446_0462_618f,
657            0x238d_5edd_f025_c62f,
658            0xf6c9_4b01_2ea9_2e72,
659            0x03ce_24ea_c1c9_3808,
660            0x0559_50f9_45da_483c,
661            0x010a_768d_0df4_eabc,
662        ]),
663    };
664    let b = Fp2 {
665        c0: Fp::from_raw_unchecked([
666            0xf05c_e7ce_9c11_39d7,
667            0x6274_8f57_97e8_a36d,
668            0xc4e8_d9df_c664_96df,
669            0xb457_88e1_8118_9209,
670            0x6949_13d0_8772_930d,
671            0x1549_836a_3770_f3cf,
672        ]),
673        c1: Fp::from_raw_unchecked([
674            0x24d0_5bb9_fb9d_491c,
675            0xfb1e_a120_c12e_39d0,
676            0x7067_879f_c807_c7b1,
677            0x60a9_269a_31bb_dab6,
678            0x45c2_56bc_fd71_649b,
679            0x18f6_9b5d_2b8a_fbde,
680        ]),
681    };
682
683    assert_eq!(-a, b);
684}
685
686#[test]
687fn test_sqrt() {
688    // a = 1488924004771393321054797166853618474668089414631333405711627789629391903630694737978065425271543178763948256226639*u + 784063022264861764559335808165825052288770346101304131934508881646553551234697082295473567906267937225174620141295
689    let a = Fp2 {
690        c0: Fp::from_raw_unchecked([
691            0x2bee_d146_27d7_f9e9,
692            0xb661_4e06_660e_5dce,
693            0x06c4_cc7c_2f91_d42c,
694            0x996d_7847_4b7a_63cc,
695            0xebae_bc4c_820d_574e,
696            0x1886_5e12_d93f_d845,
697        ]),
698        c1: Fp::from_raw_unchecked([
699            0x7d82_8664_baf4_f566,
700            0xd17e_6639_96ec_7339,
701            0x679e_ad55_cb40_78d0,
702            0xfe3b_2260_e001_ec28,
703            0x3059_93d0_43d9_1b68,
704            0x0626_f03c_0489_b72d,
705        ]),
706    };
707
708    assert_eq!(a.sqrt().unwrap().square(), a);
709
710    // b = 5, which is a generator of the p - 1 order
711    // multiplicative subgroup
712    let b = Fp2 {
713        c0: Fp::from_raw_unchecked([
714            0x6631_0000_0010_5545,
715            0x2114_0040_0eec_000d,
716            0x3fa7_af30_c820_e316,
717            0xc52a_8b8d_6387_695d,
718            0x9fb4_e61d_1e83_eac5,
719            0x005c_b922_afe8_4dc7,
720        ]),
721        c1: Fp::zero(),
722    };
723
724    assert_eq!(b.sqrt().unwrap().square(), b);
725
726    // c = 25, which is a generator of the (p - 1) / 2 order
727    // multiplicative subgroup
728    let c = Fp2 {
729        c0: Fp::from_raw_unchecked([
730            0x44f6_0000_0051_ffae,
731            0x86b8_0141_9948_0043,
732            0xd715_9952_f1f3_794a,
733            0x755d_6e3d_fe1f_fc12,
734            0xd36c_d6db_5547_e905,
735            0x02f8_c8ec_bf18_67bb,
736        ]),
737        c1: Fp::zero(),
738    };
739
740    assert_eq!(c.sqrt().unwrap().square(), c);
741
742    // 2155129644831861015726826462986972654175647013268275306775721078997042729172900466542651176384766902407257452753362*u + 2796889544896299244102912275102369318775038861758288697415827248356648685135290329705805931514906495247464901062529
743    // is nonsquare.
744    assert!(bool::from(
745        Fp2 {
746            c0: Fp::from_raw_unchecked([
747                0xc5fa_1bc8_fd00_d7f6,
748                0x3830_ca45_4606_003b,
749                0x2b28_7f11_04b1_02da,
750                0xa7fb_30f2_8230_f23e,
751                0x339c_db9e_e953_dbf0,
752                0x0d78_ec51_d989_fc57,
753            ]),
754            c1: Fp::from_raw_unchecked([
755                0x27ec_4898_cf87_f613,
756                0x9de1_394e_1abb_05a5,
757                0x0947_f85d_c170_fc14,
758                0x586f_bc69_6b61_14b7,
759                0x2b34_75a4_077d_7169,
760                0x13e1_c895_cc4b_6c22,
761            ])
762        }
763        .sqrt()
764        .is_none()
765    ));
766}
767
768#[test]
769fn test_inversion() {
770    let a = Fp2 {
771        c0: Fp::from_raw_unchecked([
772            0x1128_ecad_6754_9455,
773            0x9e7a_1cff_3a4e_a1a8,
774            0xeb20_8d51_e08b_cf27,
775            0xe98a_d408_11f5_fc2b,
776            0x736c_3a59_232d_511d,
777            0x10ac_d42d_29cf_cbb6,
778        ]),
779        c1: Fp::from_raw_unchecked([
780            0xd328_e37c_c2f5_8d41,
781            0x948d_f085_8a60_5869,
782            0x6032_f9d5_6f93_a573,
783            0x2be4_83ef_3fff_dc87,
784            0x30ef_61f8_8f48_3c2a,
785            0x1333_f55a_3572_5be0,
786        ]),
787    };
788
789    let b = Fp2 {
790        c0: Fp::from_raw_unchecked([
791            0x0581_a133_3d4f_48a6,
792            0x5824_2f6e_f074_8500,
793            0x0292_c955_349e_6da5,
794            0xba37_721d_dd95_fcd0,
795            0x70d1_6790_3aa5_dfc5,
796            0x1189_5e11_8b58_a9d5,
797        ]),
798        c1: Fp::from_raw_unchecked([
799            0x0eda_09d2_d7a8_5d17,
800            0x8808_e137_a7d1_a2cf,
801            0x43ae_2625_c1ff_21db,
802            0xf85a_c9fd_f7a7_4c64,
803            0x8fcc_dda5_b8da_9738,
804            0x08e8_4f0c_b32c_d17d,
805        ]),
806    };
807
808    assert_eq!(a.invert().unwrap(), b);
809
810    assert!(bool::from(Fp2::zero().invert().is_none()));
811}
812
813#[test]
814fn test_lexicographic_largest() {
815    assert!(!bool::from(Fp2::zero().lexicographically_largest()));
816    assert!(!bool::from(Fp2::one().lexicographically_largest()));
817    assert!(bool::from(
818        Fp2 {
819            c0: Fp::from_raw_unchecked([
820                0x1128_ecad_6754_9455,
821                0x9e7a_1cff_3a4e_a1a8,
822                0xeb20_8d51_e08b_cf27,
823                0xe98a_d408_11f5_fc2b,
824                0x736c_3a59_232d_511d,
825                0x10ac_d42d_29cf_cbb6,
826            ]),
827            c1: Fp::from_raw_unchecked([
828                0xd328_e37c_c2f5_8d41,
829                0x948d_f085_8a60_5869,
830                0x6032_f9d5_6f93_a573,
831                0x2be4_83ef_3fff_dc87,
832                0x30ef_61f8_8f48_3c2a,
833                0x1333_f55a_3572_5be0,
834            ]),
835        }
836        .lexicographically_largest()
837    ));
838    assert!(!bool::from(
839        Fp2 {
840            c0: -Fp::from_raw_unchecked([
841                0x1128_ecad_6754_9455,
842                0x9e7a_1cff_3a4e_a1a8,
843                0xeb20_8d51_e08b_cf27,
844                0xe98a_d408_11f5_fc2b,
845                0x736c_3a59_232d_511d,
846                0x10ac_d42d_29cf_cbb6,
847            ]),
848            c1: -Fp::from_raw_unchecked([
849                0xd328_e37c_c2f5_8d41,
850                0x948d_f085_8a60_5869,
851                0x6032_f9d5_6f93_a573,
852                0x2be4_83ef_3fff_dc87,
853                0x30ef_61f8_8f48_3c2a,
854                0x1333_f55a_3572_5be0,
855            ]),
856        }
857        .lexicographically_largest()
858    ));
859    assert!(!bool::from(
860        Fp2 {
861            c0: Fp::from_raw_unchecked([
862                0x1128_ecad_6754_9455,
863                0x9e7a_1cff_3a4e_a1a8,
864                0xeb20_8d51_e08b_cf27,
865                0xe98a_d408_11f5_fc2b,
866                0x736c_3a59_232d_511d,
867                0x10ac_d42d_29cf_cbb6,
868            ]),
869            c1: Fp::zero(),
870        }
871        .lexicographically_largest()
872    ));
873    assert!(bool::from(
874        Fp2 {
875            c0: -Fp::from_raw_unchecked([
876                0x1128_ecad_6754_9455,
877                0x9e7a_1cff_3a4e_a1a8,
878                0xeb20_8d51_e08b_cf27,
879                0xe98a_d408_11f5_fc2b,
880                0x736c_3a59_232d_511d,
881                0x10ac_d42d_29cf_cbb6,
882            ]),
883            c1: Fp::zero(),
884        }
885        .lexicographically_largest()
886    ));
887}
888
889#[cfg(feature = "zeroize")]
890#[test]
891fn test_zeroize() {
892    use zeroize::Zeroize;
893
894    let mut a = Fp2::one();
895    a.zeroize();
896    assert!(bool::from(a.is_zero()));
897}