halo2curves/bls12381/
g2.rs

1use core::{
2    cmp,
3    fmt::Debug,
4    iter::Sum,
5    ops::{Add, Mul, Neg, Sub},
6};
7
8use rand::RngCore;
9use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
10
11use crate::{
12    bls12381::{fq::Fq, fq2::Fq2, fr::Fr},
13    ff::{Field, PrimeField, WithSmallOrderMulGroup},
14    ff_ext::ExtField,
15    group::{cofactor::CofactorGroup, prime::PrimeCurveAffine, Curve, Group, GroupEncoding},
16    impl_binops_additive, impl_binops_additive_specify_output, impl_binops_multiplicative,
17    impl_binops_multiplicative_mixed, new_curve_impl,
18    serde::{Compressed, CompressedFlagConfig},
19    Coordinates, CurveAffine, CurveExt,
20};
21
22const G2_GENERATOR_X: Fq2 = Fq2 {
23    c0: Fq([
24        0xf5f2_8fa2_0294_0a10,
25        0xb3f5_fb26_87b4_961a,
26        0xa1a8_93b5_3e2a_e580,
27        0x9894_999d_1a3c_aee9,
28        0x6f67_b763_1863_366b,
29        0x0581_9192_4350_bcd7,
30    ]),
31    c1: Fq([
32        0xa5a9_c075_9e23_f606,
33        0xaaa0_c59d_bccd_60c3,
34        0x3bb1_7e18_e286_7806,
35        0x1b1a_b6cc_8541_b367,
36        0xc2b6_ed0e_f215_8547,
37        0x1192_2a09_7360_edf3,
38    ]),
39};
40
41const G2_GENERATOR_Y: Fq2 = Fq2 {
42    c0: Fq([
43        0x4c73_0af8_6049_4c4a,
44        0x597c_fa1f_5e36_9c5a,
45        0xe7e6_856c_aa0a_635a,
46        0xbbef_b5e9_6e0d_495f,
47        0x07d3_a975_f0ef_25a2,
48        0x0083_fd8e_7e80_dae5,
49    ]),
50    c1: Fq([
51        0xadc0_fc92_df64_b05d,
52        0x18aa_270a_2b14_61dc,
53        0x86ad_ac6a_3be4_eba0,
54        0x7949_5c4e_c93d_a33a,
55        0xe717_5850_a43c_caed,
56        0x0b2b_c2a1_63de_1bf2,
57    ]),
58};
59
60const G2_B: Fq2 = Fq2 {
61    c0: Fq::from_raw([4, 0, 0, 0, 0, 0]),
62    c1: Fq::from_raw([4, 0, 0, 0, 0, 0]),
63};
64
65const G2_A: Fq2 = Fq2::ZERO;
66
67new_curve_impl!(
68    (pub),
69    G2,
70    G2Affine,
71    Fq2,
72    Fr,
73    (G2_GENERATOR_X, G2_GENERATOR_Y),
74    G2_A,
75    G2_B,
76    "bls12381_g2",
77    |domain_prefix| hash_to_curve(domain_prefix, hash_to_curve_suite(b"BLS12381G2_XMD:SHA-256_SSWU_RO_")),
78    crate::serde::CompressedFlagConfig::ThreeSpare
79
80);
81
82impl crate::serde::endian::EndianRepr for Fq2 {
83    const ENDIAN: crate::serde::endian::Endian = Fq::ENDIAN;
84
85    fn to_bytes(&self) -> Vec<u8> {
86        self.to_bytes().to_vec()
87    }
88
89    fn from_bytes(bytes: &[u8]) -> subtle::CtOption<Self> {
90        Fq2::from_bytes(bytes[..Fq2::SIZE].try_into().unwrap())
91    }
92}
93
94impl Compressed<G2Affine> for G2Compressed {
95    const CONFIG: CompressedFlagConfig = CompressedFlagConfig::ThreeSpare;
96    fn sign(c: &G2Affine) -> Choice {
97        c.y.lexicographically_largest() & !c.is_identity()
98    }
99    fn resolve(x: Fq2, sign_set: Choice) -> CtOption<G2Affine> {
100        G2Affine::y2(x).sqrt().map(|y| {
101            let y = Fq2::conditional_select(&y, &-y, sign_set ^ y.lexicographically_largest());
102            G2Affine { x, y }
103        })
104    }
105}
106
107impl group::cofactor::CofactorGroup for G2 {
108    type Subgroup = G2;
109
110    /// Clears the cofactor, using [Budroni-Pintore](https://ia.cr/2017/419).
111    /// This is equivalent to multiplying by $h\_\textrm{eff} = 3(z^2 - 1) \cdot
112    /// h_2$, where $h_2$ is the cofactor of $\mathbb{G}\_2$ and $z$ is the
113    /// parameter of BLS12-381.
114    fn clear_cofactor(&self) -> G2 {
115        let t1 = self.mul_by_x(); // [x] P
116        let t2 = self.psi(); // psi(P)
117
118        self.double().psi2() // psi^2(2P)
119            + (t1 + t2).mul_by_x() // psi^2(2P) + [x^2] P + [x] psi(P)
120            - t1 // psi^2(2P) + [x^2 - x] P + [x] psi(P)
121            - t2 // psi^2(2P) + [x^2 - x] P + [x - 1] psi(P)
122            - self // psi^2(2P) + [x^2 - x - 1] P + [x - 1] psi(P)
123    }
124
125    fn into_subgroup(self) -> CtOption<Self::Subgroup> {
126        CtOption::new(self, 1.into())
127    }
128
129    /// Returns true if this point is free of an $h$-torsion component, and so
130    /// it exists within the $q$-order subgroup $\mathbb{G}_2$. This should
131    /// always return true unless an "unchecked" API was used.
132    fn is_torsion_free(&self) -> Choice {
133        // Algorithm from Section 4 of https://eprint.iacr.org/2021/1130
134        // Updated proof of correctness in https://eprint.iacr.org/2022/352
135        //
136        // Check that psi(P) == [x] P
137        self.psi().ct_eq(&self.mul_by_x())
138    }
139}
140
141impl G2 {
142    /// Multiply `self` by `crate::BLS_X`, using double and add.
143
144    fn mul_by_x(&self) -> G2 {
145        let mut acc = G2::identity();
146        for (i, b) in super::BLS_X.into_iter().enumerate() {
147            (i != 0).then(|| acc = acc.double());
148            (b == 1).then(|| acc += self);
149        }
150        acc.neg()
151    }
152
153    fn psi(&self) -> G2 {
154        // 1 / ((u+1) ^ ((q-1)/3))
155        let psi_coeff_x = Fq2 {
156            c0: Fq::zero(),
157            c1: Fq([
158                0x890dc9e4867545c3,
159                0x2af322533285a5d5,
160                0x50880866309b7e2c,
161                0xa20d1b8c7e881024,
162                0x14e4f04fe2db9068,
163                0x14e56d3f1564853a,
164            ]),
165        };
166        // 1 / ((u+1) ^ (p-1)/2)
167        let psi_coeff_y = Fq2 {
168            c0: Fq([
169                0x3e2f585da55c9ad1,
170                0x4294213d86c18183,
171                0x382844c88b623732,
172                0x92ad2afd19103e18,
173                0x1d794e4fac7cf0b9,
174                0x0bd592fc7d825ec8,
175            ]),
176            c1: Fq([
177                0x7bcfa7a25aa30fda,
178                0xdc17dec12a927e7c,
179                0x2f088dd86b4ebef1,
180                0xd1ca2087da74d4a7,
181                0x2da2596696cebc1d,
182                0x0e2b7eedbbfd87d2,
183            ]),
184        };
185
186        // x = frobenius(x)/((u+1)^((p-1)/3))
187        let mut x = self.x;
188        x.frobenius_map(1);
189        x.mul_assign(&psi_coeff_x);
190
191        // y = frobenius(y)/(u+1)^((p-1)/2)
192        let mut y = self.y;
193        y.frobenius_map(1);
194        y.mul_assign(&psi_coeff_y);
195
196        // z = frobenius(z)
197        let mut z = self.z;
198        z.frobenius_map(1);
199
200        G2 { x, y, z }
201    }
202
203    fn psi2(&self) -> G2 {
204        // 1 / 2 ^ ((q-1)/3)
205        let psi2_coeff_x = Fq2 {
206            c0: Fq([
207                0xcd03c9e48671f071,
208                0x5dab22461fcda5d2,
209                0x587042afd3851b95,
210                0x8eb60ebe01bacb9e,
211                0x03f97d6e83d050d2,
212                0x18f0206554638741,
213            ]),
214            c1: Fq::zero(),
215        };
216
217        G2 {
218            // x = frobenius^2(x)/2^((p-1)/3); note that q^2 is the order of the field.
219            x: self.x * psi2_coeff_x,
220            // y = -frobenius^2(y); note that q^2 is the order of the field.
221            y: self.y.neg(),
222            // z = z
223            z: self.z,
224        }
225    }
226}
227
228fn hash_to_curve_suite(domain: &[u8]) -> crate::hash_to_curve::Suite<G2, sha2::Sha256, 128> {
229    const SSWU_Z: Fq2 = Fq2 {
230        c0: Fq([
231            0x87eb_ffff_fff9_555c,
232            0x656f_ffe5_da8f_fffa,
233            0x0fd0_7493_45d3_3ad2,
234            0xd951_e663_0665_76f4,
235            0xde29_1a3d_41e9_80d3,
236            0x0815_664c_7dfe_040d,
237        ]),
238        c1: Fq([
239            0x43f5_ffff_fffc_aaae,
240            0x32b7_fff2_ed47_fffd,
241            0x07e8_3a49_a2e9_9d69,
242            0xeca8_f331_8332_bb7a,
243            0xef14_8d1e_a0f4_c069,
244            0x040a_b326_3eff_0206,
245        ]),
246    };
247
248    const ISO_A: Fq2 = Fq2 {
249        c0: Fq::zero(),
250        c1: Fq([
251            0xe53a_0000_0313_5242,
252            0x0108_0c0f_def8_0285,
253            0xe788_9edb_e340_f6bd,
254            0x0b51_3751_2631_0601,
255            0x02d6_9857_17c7_44ab,
256            0x1220_b4e9_79ea_5467,
257        ]),
258    };
259
260    const ISO_B: Fq2 = Fq2 {
261        c0: Fq([
262            0x22ea_0000_0cf8_9db2,
263            0x6ec8_32df_7138_0aa4,
264            0x6e1b_9440_3db5_a66e,
265            0x75bf_3c53_a794_73ba,
266            0x3dd3_a569_412c_0a34,
267            0x125c_db5e_74dc_4fd1,
268        ]),
269        c1: Fq([
270            0x22ea_0000_0cf8_9db2,
271            0x6ec8_32df_7138_0aa4,
272            0x6e1b_9440_3db5_a66e,
273            0x75bf_3c53_a794_73ba,
274            0x3dd3_a569_412c_0a34,
275            0x125c_db5e_74dc_4fd1,
276        ]),
277    };
278    let iso_map = crate::hash_to_curve::Iso {
279        a: ISO_A,
280        b: ISO_B,
281        map: Box::new(iso_map),
282    };
283
284    crate::hash_to_curve::Suite::new(domain, SSWU_Z, crate::hash_to_curve::Method::SSWU(iso_map))
285}
286
287/// Maps an iso-G1 point to a G1 point.
288fn iso_map(x: Fq2, y: Fq2, z: Fq2) -> G2 {
289    const COEFFS: [&[Fq2]; 4] = [&ISO3_XNUM, &ISO3_XDEN, &ISO3_YNUM, &ISO3_YDEN];
290
291    // xnum, xden, ynum, yden
292    let mut mapvals = [Fq2::ZERO; 4];
293
294    // compute powers of z
295    let zsq = z.square();
296    let zpows = [z, zsq, zsq * z];
297
298    // compute map value by Horner's rule
299    for idx in 0..4 {
300        let coeff = COEFFS[idx];
301        let clast = coeff.len() - 1;
302        mapvals[idx] = coeff[clast];
303        for jdx in 0..clast {
304            mapvals[idx] = mapvals[idx] * x + zpows[jdx] * coeff[clast - 1 - jdx];
305        }
306    }
307
308    // x denominator is order 1 less than x numerator, so we need an extra factor of
309    // z
310    mapvals[1] *= z;
311
312    // multiply result of Y map by the y-coord, y / z
313    mapvals[2] *= y;
314    mapvals[3] *= z;
315
316    G2 {
317        x: mapvals[0] * mapvals[3], // xnum * yden,
318        y: mapvals[2] * mapvals[1], // ynum * xden,
319        z: mapvals[1] * mapvals[3], // xden * yden
320    }
321}
322
323#[allow(clippy::type_complexity)]
324pub(crate) fn hash_to_curve<'a>(
325    domain_prefix: &'a str,
326    suite: crate::hash_to_curve::Suite<G2, sha2::Sha256, 128>,
327) -> Box<dyn Fn(&[u8]) -> G2 + 'a> {
328    Box::new(move |message| suite.hash_to_curve(domain_prefix, message).clear_cofactor())
329}
330
331#[cfg(test)]
332mod test {
333    use group::UncompressedEncoding;
334    use rand_core::OsRng;
335
336    use super::*;
337    use crate::{arithmetic::CurveEndo, serde::SerdeObject};
338
339    crate::curve_testing_suite!(G2);
340    crate::curve_testing_suite!(G2, "endo_consistency");
341    crate::curve_testing_suite!(G2, "endo");
342
343    #[test]
344    fn test_cofactor() {
345        assert!(bool::from(
346            G2Affine::identity().to_curve().is_torsion_free()
347        ));
348        assert!(bool::from(
349            G2Affine::generator().to_curve().is_torsion_free()
350        ));
351    }
352
353    #[test]
354    fn test_hash_to_curve() {
355        pub(crate) fn point_from_hex(x0: &str, x1: &str, y0: &str, y1: &str) -> G2Affine {
356            let x0: Fq = crate::tests::hex_to_field(x0);
357            let x1: Fq = crate::tests::hex_to_field(x1);
358            let x = Fq2 { c0: x0, c1: x1 };
359            let y0: Fq = crate::tests::hex_to_field(y0);
360            let y1: Fq = crate::tests::hex_to_field(y1);
361            let y = Fq2 { c0: y0, c1: y1 };
362            G2Affine::from_xy(x, y).unwrap()
363        }
364
365        struct Test {
366            msg: &'static [u8],
367            expect: G2Affine,
368        }
369
370        impl Test {
371            fn new(msg: &'static [u8], expect: G2Affine) -> Self {
372                Self { msg, expect }
373            }
374
375            fn run(&self, domain_prefix: &str) {
376                let r0 = G2::hash_to_curve(domain_prefix)(self.msg);
377                assert_eq!(r0.to_affine(), self.expect);
378            }
379        }
380
381        let tests = [
382        Test::new(
383            b"",
384            point_from_hex(
385                "0141ebfbdca40eb85b87142e130ab689c673cf60f1a3e98d69335266f30d9b8d4ac44c1038e9dcdd5393faf5c41fb78a",
386                "05cb8437535e20ecffaef7752baddf98034139c38452458baeefab379ba13dff5bf5dd71b72418717047f5b0f37da03d",
387                "0503921d7f6a12805e72940b963c0cf3471c7b2a524950ca195d11062ee75ec076daf2d4bc358c4b190c0c98064fdd92",
388                "12424ac32561493f3fe3c260708a12b7c620e7be00099a974e259ddc7d1f6395c3c811cdd19f1e8dbf3e9ecfdcbab8d6",
389            ),
390        ),
391        Test::new(
392            b"abc",
393            point_from_hex(
394                "02c2d18e033b960562aae3cab37a27ce00d80ccd5ba4b7fe0e7a210245129dbec7780ccc7954725f4168aff2787776e6",
395                "139cddbccdc5e91b9623efd38c49f81a6f83f175e80b06fc374de9eb4b41dfe4ca3a230ed250fbe3a2acf73a41177fd8",
396                "1787327b68159716a37440985269cf584bcb1e621d3a7202be6ea05c4cfe244aeb197642555a0645fb87bf7466b2ba48",
397                "00aa65dae3c8d732d10ecd2c50f8a1baf3001578f71c694e03866e9f3d49ac1e1ce70dd94a733534f106d4cec0eddd16",
398            ),
399        ),
400        Test::new(
401            b"abcdef0123456789",
402            point_from_hex(
403                "121982811d2491fde9ba7ed31ef9ca474f0e1501297f68c298e9f4c0028add35aea8bb83d53c08cfc007c1e005723cd0",
404                "190d119345b94fbd15497bcba94ecf7db2cbfd1e1fe7da034d26cbba169fb3968288b3fafb265f9ebd380512a71c3f2c",
405                "05571a0f8d3c08d094576981f4a3b8eda0a8e771fcdcc8ecceaf1356a6acf17574518acb506e435b639353c2e14827c8",
406                "0bb5e7572275c567462d91807de765611490205a941a5a6af3b1691bfe596c31225d3aabdf15faff860cb4ef17c7c3be",
407            ),
408        ),
409        Test::new(
410            b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
411            point_from_hex(
412                "19a84dd7248a1066f737cc34502ee5555bd3c19f2ecdb3c7d9e24dc65d4e25e50d83f0f77105e955d78f4762d33c17da",
413                "0934aba516a52d8ae479939a91998299c76d39cc0c035cd18813bec433f587e2d7a4fef038260eef0cef4d02aae3eb91",
414                "14f81cd421617428bc3b9fe25afbb751d934a00493524bc4e065635b0555084dd54679df1536101b2c979c0152d09192",
415                "09bcccfa036b4847c9950780733633f13619994394c23ff0b32fa6b795844f4a0673e20282d07bc69641cee04f5e5662",
416            ),
417        ),
418        Test::new(
419            b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
420            point_from_hex(
421                "01a6ba2f9a11fa5598b2d8ace0fbe0a0eacb65deceb476fbbcb64fd24557c2f4b18ecfc5663e54ae16a84f5ab7f62534",
422                "11fca2ff525572795a801eed17eb12785887c7b63fb77a42be46ce4a34131d71f7a73e95fee3f812aea3de78b4d01569",
423                "0b6798718c8aed24bc19cb27f866f1c9effcdbf92397ad6448b5c9db90d2b9da6cbabf48adc1adf59a1a28344e79d57e",
424                "03a47f8e6d1763ba0cad63d6114c0accbef65707825a511b251a660a9b3994249ae4e63fac38b23da0c398689ee2ab52",
425            ),
426        ),
427    ];
428
429        tests.iter().for_each(|test| {
430            test.run("QUUX-V01-CS02-with-");
431        });
432    }
433}
434
435/// Coefficients of the 3-isogeny x map's numerator
436const ISO3_XNUM: [Fq2; 4] = [
437    Fq2 {
438        c0: Fq([
439            0x47f6_71c7_1ce0_5e62,
440            0x06dd_5707_1206_393e,
441            0x7c80_cd2a_f3fd_71a2,
442            0x0481_03ea_9e6c_d062,
443            0xc545_16ac_c8d0_37f6,
444            0x1380_8f55_0920_ea41,
445        ]),
446        c1: Fq([
447            0x47f6_71c7_1ce0_5e62,
448            0x06dd_5707_1206_393e,
449            0x7c80_cd2a_f3fd_71a2,
450            0x0481_03ea_9e6c_d062,
451            0xc545_16ac_c8d0_37f6,
452            0x1380_8f55_0920_ea41,
453        ]),
454    },
455    Fq2 {
456        c0: Fq::zero(),
457        c1: Fq([
458            0x5fe5_5555_554c_71d0,
459            0x873f_ffdd_236a_aaa3,
460            0x6a6b_4619_b26e_f918,
461            0x21c2_8884_0887_4945,
462            0x2836_cda7_028c_abc5,
463            0x0ac7_3310_a7fd_5abd,
464        ]),
465    },
466    Fq2 {
467        c0: Fq([
468            0x0a0c_5555_5559_71c3,
469            0xdb0c_0010_1f9e_aaae,
470            0xb1fb_2f94_1d79_7997,
471            0xd396_0742_ef41_6e1c,
472            0xb700_40e2_c205_56f4,
473            0x149d_7861_e581_393b,
474        ]),
475        c1: Fq([
476            0xaff2_aaaa_aaa6_38e8,
477            0x439f_ffee_91b5_5551,
478            0xb535_a30c_d937_7c8c,
479            0x90e1_4442_0443_a4a2,
480            0x941b_66d3_8146_55e2,
481            0x0563_9988_53fe_ad5e,
482        ]),
483    },
484    Fq2 {
485        c0: Fq([
486            0x40aa_c71c_71c7_25ed,
487            0x1909_5555_7a84_e38e,
488            0xd817_050a_8f41_abc3,
489            0xd864_85d4_c87f_6fb1,
490            0x696e_b479_f885_d059,
491            0x198e_1a74_3280_02d2,
492        ]),
493        c1: Fq::zero(),
494    },
495];
496
497/// Coefficients of the 3-isogeny x map's denominator
498const ISO3_XDEN: [Fq2; 3] = [
499    Fq2 {
500        c0: Fq::zero(),
501        c1: Fq([
502            0x1f3a_ffff_ff13_ab97,
503            0xf25b_fc61_1da3_ff3e,
504            0xca37_57cb_3819_b208,
505            0x3e64_2736_6f8c_ec18,
506            0x0397_7bc8_6095_b089,
507            0x04f6_9db1_3f39_a952,
508        ]),
509    },
510    Fq2 {
511        c0: Fq([
512            0x4476_0000_0027_552e,
513            0xdcb8_009a_4348_0020,
514            0x6f7e_e9ce_4a6e_8b59,
515            0xb103_30b7_c0a9_5bc6,
516            0x6140_b1fc_fb1e_54b7,
517            0x0381_be09_7f0b_b4e1,
518        ]),
519        c1: Fq([
520            0x7588_ffff_ffd8_557d,
521            0x41f3_ff64_6e0b_ffdf,
522            0xf7b1_e8d2_ac42_6aca,
523            0xb374_1acd_32db_b6f8,
524            0xe9da_f5b9_482d_581f,
525            0x167f_53e0_ba74_31b8,
526        ]),
527    },
528    Fq2::one(),
529];
530
531/// Coefficients of the 3-isogeny y map's numerator
532const ISO3_YNUM: [Fq2; 4] = [
533    Fq2 {
534        c0: Fq([
535            0x96d8_f684_bdfc_77be,
536            0xb530_e4f4_3b66_d0e2,
537            0x184a_88ff_3796_52fd,
538            0x57cb_23ec_fae8_04e1,
539            0x0fd2_e39e_ada3_eba9,
540            0x08c8_055e_31c5_d5c3,
541        ]),
542        c1: Fq([
543            0x96d8_f684_bdfc_77be,
544            0xb530_e4f4_3b66_d0e2,
545            0x184a_88ff_3796_52fd,
546            0x57cb_23ec_fae8_04e1,
547            0x0fd2_e39e_ada3_eba9,
548            0x08c8_055e_31c5_d5c3,
549        ]),
550    },
551    Fq2 {
552        c0: Fq::zero(),
553        c1: Fq([
554            0xbf0a_71c7_1c91_b406,
555            0x4d6d_55d2_8b76_38fd,
556            0x9d82_f98e_5f20_5aee,
557            0xa27a_a27b_1d1a_18d5,
558            0x02c3_b2b2_d293_8e86,
559            0x0c7d_1342_0b09_807f,
560        ]),
561    },
562    Fq2 {
563        c0: Fq([
564            0xd7f9_5555_5553_1c74,
565            0x21cf_fff7_48da_aaa8,
566            0x5a9a_d186_6c9b_be46,
567            0x4870_a221_0221_d251,
568            0x4a0d_b369_c0a3_2af1,
569            0x02b1_ccc4_29ff_56af,
570        ]),
571        c1: Fq([
572            0xe205_aaaa_aaac_8e37,
573            0xfcdc_0007_6879_5556,
574            0x0c96_011a_8a15_37dd,
575            0x1c06_a963_f163_406e,
576            0x010d_f44c_82a8_81e6,
577            0x174f_4526_0f80_8feb,
578        ]),
579    },
580    Fq2 {
581        c0: Fq([
582            0xa470_bda1_2f67_f35c,
583            0xc0fe_38e2_3327_b425,
584            0xc9d3_d0f2_c6f0_678d,
585            0x1c55_c993_5b5a_982e,
586            0x27f6_c0e2_f074_6764,
587            0x117c_5e6e_28aa_9054,
588        ]),
589        c1: Fq::zero(),
590    },
591];
592
593/// Coefficients of the 3-isogeny y map's denominator
594const ISO3_YDEN: [Fq2; 4] = [
595    Fq2 {
596        c0: Fq([
597            0x0162_ffff_fa76_5adf,
598            0x8f7b_ea48_0083_fb75,
599            0x561b_3c22_59e9_3611,
600            0x11e1_9fc1_a9c8_75d5,
601            0xca71_3efc_0036_7660,
602            0x03c6_a03d_41da_1151,
603        ]),
604        c1: Fq([
605            0x0162_ffff_fa76_5adf,
606            0x8f7b_ea48_0083_fb75,
607            0x561b_3c22_59e9_3611,
608            0x11e1_9fc1_a9c8_75d5,
609            0xca71_3efc_0036_7660,
610            0x03c6_a03d_41da_1151,
611        ]),
612    },
613    Fq2 {
614        c0: Fq::zero(),
615        c1: Fq([
616            0x5db0_ffff_fd3b_02c5,
617            0xd713_f523_58eb_fdba,
618            0x5ea6_0761_a84d_161a,
619            0xbb2c_75a3_4ea6_c44a,
620            0x0ac6_7359_21c1_119b,
621            0x0ee3_d913_bdac_fbf6,
622        ]),
623    },
624    Fq2 {
625        c0: Fq([
626            0x66b1_0000_003a_ffc5,
627            0xcb14_00e7_64ec_0030,
628            0xa73e_5eb5_6fa5_d106,
629            0x8984_c913_a0fe_09a9,
630            0x11e1_0afb_78ad_7f13,
631            0x0542_9d0e_3e91_8f52,
632        ]),
633        c1: Fq([
634            0x534d_ffff_ffc4_aae6,
635            0x5397_ff17_4c67_ffcf,
636            0xbff2_73eb_870b_251d,
637            0xdaf2_8271_5287_0915,
638            0x393a_9cba_ca9e_2dc3,
639            0x14be_74db_faee_5748,
640        ]),
641    },
642    Fq2::one(),
643];