1use super::fq::Fq;
2use super::fq2::Fq2;
3use super::fq6::Fq6;
4use crate::ff::Field;
5use core::ops::{Add, Mul, Neg, Sub};
6use rand::RngCore;
7use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
8
9#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
15pub struct Fq12 {
16 pub c0: Fq6,
17 pub c1: Fq6,
18}
19
20impl ConditionallySelectable for Fq12 {
21 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
22 Fq12 {
23 c0: Fq6::conditional_select(&a.c0, &b.c0, choice),
24 c1: Fq6::conditional_select(&a.c1, &b.c1, choice),
25 }
26 }
27}
28
29impl ConstantTimeEq for Fq12 {
30 fn ct_eq(&self, other: &Self) -> Choice {
31 self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1)
32 }
33}
34
35impl Neg for Fq12 {
36 type Output = Fq12;
37
38 #[inline]
39 fn neg(self) -> Fq12 {
40 -&self
41 }
42}
43
44impl<'a> Neg for &'a Fq12 {
45 type Output = Fq12;
46
47 #[inline]
48 fn neg(self) -> Fq12 {
49 self.neg()
50 }
51}
52
53impl<'a, 'b> Sub<&'b Fq12> for &'a Fq12 {
54 type Output = Fq12;
55
56 #[inline]
57 fn sub(self, rhs: &'b Fq12) -> Fq12 {
58 self.sub(rhs)
59 }
60}
61
62impl<'a, 'b> Add<&'b Fq12> for &'a Fq12 {
63 type Output = Fq12;
64
65 #[inline]
66 fn add(self, rhs: &'b Fq12) -> Fq12 {
67 self.add(rhs)
68 }
69}
70
71impl<'a, 'b> Mul<&'b Fq12> for &'a Fq12 {
72 type Output = Fq12;
73
74 #[inline]
75 fn mul(self, rhs: &'b Fq12) -> Fq12 {
76 self.mul(rhs)
77 }
78}
79
80use crate::{
81 impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output,
82 impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output,
83 impl_sum_prod,
84};
85impl_binops_additive!(Fq12, Fq12);
86impl_binops_multiplicative!(Fq12, Fq12);
87impl_sum_prod!(Fq12);
88
89impl Fq12 {
90 #[inline]
91 pub const fn zero() -> Self {
92 Fq12 {
93 c0: Fq6::ZERO,
94 c1: Fq6::ZERO,
95 }
96 }
97
98 #[inline]
99 pub const fn one() -> Self {
100 Fq12 {
101 c0: Fq6::ONE,
102 c1: Fq6::ZERO,
103 }
104 }
105
106 pub fn mul_assign(&mut self, other: &Self) {
107 let t0 = self.c0 * other.c0;
108 let mut t1 = self.c1 * other.c1;
109 let t2 = other.c0 + other.c1;
110
111 self.c1 += &self.c0;
112 self.c1 *= &t2;
113 self.c1 -= &t0;
114 self.c1 -= &t1;
115
116 t1.mul_by_nonresidue();
117 self.c0 = t0 + t1;
118 }
119
120 pub fn square_assign(&mut self) {
121 let mut ab = self.c0 * self.c1;
122
123 let c0c1 = self.c0 + self.c1;
124
125 let mut c0 = self.c1;
126 c0.mul_by_nonresidue();
127 c0 += &self.c0;
128 c0 *= &c0c1;
129 c0 -= &ab;
130 self.c1 = ab;
131 self.c1 += &ab;
132 ab.mul_by_nonresidue();
133 c0 -= &ab;
134 self.c0 = c0;
135 }
136
137 pub fn double(&self) -> Self {
138 Self {
139 c0: self.c0.double(),
140 c1: self.c1.double(),
141 }
142 }
143
144 pub fn double_assign(&mut self) {
145 self.c0 = self.c0.double();
146 self.c1 = self.c1.double();
147 }
148
149 pub fn add(&self, other: &Self) -> Self {
150 Self {
151 c0: self.c0 + other.c0,
152 c1: self.c1 + other.c1,
153 }
154 }
155
156 pub fn sub(&self, other: &Self) -> Self {
157 Self {
158 c0: self.c0 - other.c0,
159 c1: self.c1 - other.c1,
160 }
161 }
162
163 pub fn mul(&self, other: &Self) -> Self {
164 let mut t = *other;
165 t.mul_assign(self);
166 t
167 }
168
169 pub fn square(&self) -> Self {
170 let mut t = *self;
171 t.square_assign();
172 t
173 }
174
175 #[inline(always)]
176 pub fn neg(&self) -> Self {
177 Self {
178 c0: -self.c0,
179 c1: -self.c1,
180 }
181 }
182
183 #[inline(always)]
184 pub fn conjugate(&mut self) {
185 self.c1 = -self.c1;
186 }
187
188 pub fn frobenius_map(&mut self, power: usize) {
196 self.c0.frobenius_map(power);
197 self.c1.frobenius_map(power);
198
199 self.c1.c0.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]);
200 self.c1.c1.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]);
201 self.c1.c2.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]);
202 }
203
204 pub fn mul_by_014(&mut self, c0: &Fq2, c1: &Fq2, c4: &Fq2) {
205 let mut aa = self.c0;
206 aa.mul_by_01(c0, c1);
207 let mut bb = self.c1;
208 bb.mul_by_1(c4);
209 let o = c1 + c4;
210 self.c1 += &self.c0;
211 self.c1.mul_by_01(c0, &o);
212 self.c1 -= &aa;
213 self.c1 -= &bb;
214 self.c0 = bb;
215 self.c0.mul_by_nonresidue();
216 self.c0 += &aa;
217 }
218
219 pub fn mul_by_034(&mut self, c0: &Fq2, c3: &Fq2, c4: &Fq2) {
220 let t0 = Fq6 {
221 c0: self.c0.c0 * c0,
222 c1: self.c0.c1 * c0,
223 c2: self.c0.c2 * c0,
224 };
225 let mut t1 = self.c1;
226 t1.mul_by_01(c3, c4);
227 let o = c0 + c3;
228 let mut t2 = self.c0 + self.c1;
229 t2.mul_by_01(&o, c4);
230 t2 -= t0;
231 self.c1 = t2 - t1;
232 t1.mul_by_nonresidue();
233 self.c0 = t0 + t1;
234 }
235
236 pub fn invert(&self) -> CtOption<Self> {
237 let mut c0s = self.c0;
238 c0s.square_assign();
239 let mut c1s = self.c1;
240 c1s.square_assign();
241 c1s.mul_by_nonresidue();
242 c0s -= &c1s;
243
244 c0s.invert().map(|t| {
245 let mut tmp = Fq12 { c0: t, c1: t };
246 tmp.c0.mul_assign(&self.c0);
247 tmp.c1.mul_assign(&self.c1);
248 tmp.c1 = tmp.c1.neg();
249
250 tmp
251 })
252 }
253
254 pub fn cyclotomic_square(&mut self) {
255 fn fp4_square(c0: &mut Fq2, c1: &mut Fq2, a0: &Fq2, a1: &Fq2) {
256 let t0 = a0.square();
257 let t1 = a1.square();
258 let mut t2 = t1;
259 t2.mul_by_nonresidue();
260 *c0 = t2 + t0;
261 t2 = a0 + a1;
262 t2.square_assign();
263 t2 -= t0;
264 *c1 = t2 - t1;
265 }
266
267 let mut t3 = Fq2::zero();
268 let mut t4 = Fq2::zero();
269 let mut t5 = Fq2::zero();
270 let mut t6 = Fq2::zero();
271
272 fp4_square(&mut t3, &mut t4, &self.c0.c0, &self.c1.c1);
273 let mut t2 = t3 - self.c0.c0;
274 t2.double_assign();
275 self.c0.c0 = t2 + t3;
276
277 t2 = t4 + self.c1.c1;
278 t2.double_assign();
279 self.c1.c1 = t2 + t4;
280
281 fp4_square(&mut t3, &mut t4, &self.c1.c0, &self.c0.c2);
282 fp4_square(&mut t5, &mut t6, &self.c0.c1, &self.c1.c2);
283
284 t2 = t3 - self.c0.c1;
285 t2.double_assign();
286 self.c0.c1 = t2 + t3;
287 t2 = t4 + self.c1.c2;
288 t2.double_assign();
289 self.c1.c2 = t2 + t4;
290 t3 = t6;
291 t3.mul_by_nonresidue();
292 t2 = t3 + self.c1.c0;
293 t2.double_assign();
294 self.c1.c0 = t2 + t3;
295 t2 = t5 - self.c0.c2;
296 t2.double_assign();
297 self.c0.c2 = t2 + t5;
298 }
299}
300
301impl Field for Fq12 {
302 const ZERO: Self = Self::zero();
303 const ONE: Self = Self::one();
304
305 fn random(mut rng: impl RngCore) -> Self {
306 Fq12 {
307 c0: Fq6::random(&mut rng),
308 c1: Fq6::random(&mut rng),
309 }
310 }
311
312 fn is_zero(&self) -> Choice {
313 self.c0.is_zero() & self.c1.is_zero()
314 }
315
316 fn square(&self) -> Self {
317 self.square()
318 }
319
320 fn double(&self) -> Self {
321 self.double()
322 }
323
324 fn sqrt(&self) -> CtOption<Self> {
325 unimplemented!()
326 }
327
328 fn sqrt_ratio(_num: &Self, _div: &Self) -> (Choice, Self) {
329 unimplemented!()
330 }
331
332 fn invert(&self) -> CtOption<Self> {
333 self.invert()
334 }
335}
336
337pub const FROBENIUS_COEFF_FQ12_C1: [Fq2; 12] = [
339 Fq2 {
342 c0: Fq([
343 0xd35d438dc58f0d9d,
344 0x0a78eb28f5c70b3d,
345 0x666ea36f7879462c,
346 0x0e0a77c19a07df2f,
347 ]),
348 c1: Fq([0x0, 0x0, 0x0, 0x0]),
349 },
350 Fq2 {
352 c0: Fq([
353 0xaf9ba69633144907,
354 0xca6b1d7387afb78a,
355 0x11bded5ef08a2087,
356 0x02f34d751a1f3a7c,
357 ]),
358 c1: Fq([
359 0xa222ae234c492d72,
360 0xd00f02a4565de15b,
361 0xdc2ff3a253dfc926,
362 0x10a75716b3899551,
363 ]),
364 },
365 Fq2 {
367 c0: Fq([
368 0xca8d800500fa1bf2,
369 0xf0c5d61468b39769,
370 0x0e201271ad0d4418,
371 0x04290f65bad856e6,
372 ]),
373 c1: Fq([0x0, 0x0, 0x0, 0x0]),
374 },
375 Fq2 {
377 c0: Fq([
378 0x365316184e46d97d,
379 0x0af7129ed4c96d9f,
380 0x659da72fca1009b5,
381 0x08116d8983a20d23,
382 ]),
383 c1: Fq([
384 0xb1df4af7c39c1939,
385 0x3d9f02878a73bf7f,
386 0x9b2220928caf0ae0,
387 0x26684515eff054a6,
388 ]),
389 },
390 Fq2 {
392 c0: Fq([
393 0x3350c88e13e80b9c,
394 0x7dce557cdb5e56b9,
395 0x6001b4b8b615564a,
396 0x2682e617020217e0,
397 ]),
398 c1: Fq([0x0, 0x0, 0x0, 0x0]),
399 },
400 Fq2 {
402 c0: Fq([
403 0x86b76f821b329076,
404 0x408bf52b4d19b614,
405 0x53dfb9d0d985e92d,
406 0x051e20146982d2a7,
407 ]),
408 c1: Fq([
409 0x0fbc9cd47752ebc7,
410 0x6d8fffe33415de24,
411 0xbef22cf038cf41b9,
412 0x15c0edff3c66bf54,
413 ]),
414 },
415 Fq2 {
417 c0: Fq([
418 0x68c3488912edefaa,
419 0x8d087f6872aabf4f,
420 0x51e1a24709081231,
421 0x2259d6b14729c0fa,
422 ]),
423 c1: Fq([0x0, 0x0, 0x0, 0x0]),
424 },
425 Fq2 {
427 c0: Fq([
428 0x8c84e580a568b440,
429 0xcd164d1de0c21302,
430 0xa692585790f737d5,
431 0x2d7100fdc71265ad,
432 ]),
433 c1: Fq([
434 0x99fdddf38c33cfd5,
435 0xc77267ed1213e931,
436 0xdc2052142da18f36,
437 0x1fbcf75c2da80ad7,
438 ]),
439 },
440 Fq2 {
442 c0: Fq([
443 0x71930c11d782e155,
444 0xa6bb947cffbe3323,
445 0xaa303344d4741444,
446 0x2c3b3f0d26594943,
447 ]),
448 c1: Fq([0x0, 0x0, 0x0, 0x0]),
449 },
450 Fq2 {
452 c0: Fq([
453 0x05cd75fe8a3623ca,
454 0x8c8a57f293a85cee,
455 0x52b29e86b7714ea8,
456 0x2852e0e95d8f9306,
457 ]),
458 c1: Fq([
459 0x8a41411f14e0e40e,
460 0x59e26809ddfe0b0d,
461 0x1d2e2523f4d24d7d,
462 0x09fc095cf1414b83,
463 ]),
464 },
465 Fq2 {
467 c0: Fq([
468 0x08cfc388c494f1ab,
469 0x19b315148d1373d4,
470 0x584e90fdcb6c0213,
471 0x09e1685bdf2f8849,
472 ]),
473 c1: Fq([0x0, 0x0, 0x0, 0x0]),
474 },
475 Fq2 {
477 c0: Fq([
478 0xb5691c94bd4a6cd1,
479 0x56f575661b581478,
480 0x64708be5a7fb6f30,
481 0x2b462e5e77aecd82,
482 ]),
483 c1: Fq([
484 0x2c63ef42612a1180,
485 0x29f16aae345bec69,
486 0xf95e18c648b216a4,
487 0x1aa36073a4cae0d4,
488 ]),
489 },
490];
491
492#[cfg(test)]
493use rand::SeedableRng;
494#[cfg(test)]
495use rand_xorshift::XorShiftRng;
496
497#[test]
498fn test_fq12_mul_by_014() {
499 let mut rng = XorShiftRng::from_seed([
500 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
501 0xe5,
502 ]);
503
504 for _ in 0..1000 {
505 let c0 = Fq2::random(&mut rng);
506 let c1 = Fq2::random(&mut rng);
507 let c5 = Fq2::random(&mut rng);
508 let mut a = Fq12::random(&mut rng);
509 let mut b = a;
510
511 a.mul_by_014(&c0, &c1, &c5);
512 b.mul_assign(&Fq12 {
513 c0: Fq6 {
514 c0,
515 c1,
516 c2: Fq2::zero(),
517 },
518 c1: Fq6 {
519 c0: Fq2::zero(),
520 c1: c5,
521 c2: Fq2::zero(),
522 },
523 });
524
525 assert_eq!(a, b);
526 }
527}
528
529#[test]
530fn test_fq12_mul_by_034() {
531 let mut rng = XorShiftRng::from_seed([
532 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
533 0xe5,
534 ]);
535
536 for _ in 0..1000 {
537 let c0 = Fq2::random(&mut rng);
538 let c3 = Fq2::random(&mut rng);
539 let c4 = Fq2::random(&mut rng);
540 let mut a = Fq12::random(&mut rng);
541 let mut b = a;
542
543 a.mul_by_034(&c0, &c3, &c4);
544 b.mul_assign(&Fq12 {
545 c0: Fq6 {
546 c0,
547 c1: Fq2::zero(),
548 c2: Fq2::zero(),
549 },
550 c1: Fq6 {
551 c0: c3,
552 c1: c4,
553 c2: Fq2::zero(),
554 },
555 });
556
557 assert_eq!(a, b);
558 }
559}
560
561#[test]
562fn test_squaring() {
563 let mut rng = XorShiftRng::from_seed([
564 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
565 0xe5,
566 ]);
567
568 for _ in 0..1000 {
569 let mut a = Fq12::random(&mut rng);
570 let mut b = a;
571 b.mul_assign(&a);
572 a.square_assign();
573 assert_eq!(a, b);
574 }
575}
576
577#[test]
578fn test_frobenius() {
579 let mut rng = XorShiftRng::from_seed([
580 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
581 0xe5,
582 ]);
583
584 for _ in 0..100 {
585 for i in 0..14 {
586 let mut a = Fq12::random(&mut rng);
587 let mut b = a;
588
589 for _ in 0..i {
590 a = a.pow_vartime([
591 0x3c208c16d87cfd47,
592 0x97816a916871ca8d,
593 0xb85045b68181585d,
594 0x30644e72e131a029,
595 ]);
596 }
597 b.frobenius_map(i);
598
599 assert_eq!(a, b);
600 }
601 }
602}
603
604#[test]
605fn test_field() {
606 crate::tests::field::random_field_tests::<Fq12>("fq12".to_string());
607}