1use crate::field_ops::{FieldFromRepr, FieldOps, FieldRandom};
4use core::ops::{Add, Mul, Neg, Sub};
5use crypto_bigint::Uint;
6use std::marker::PhantomData;
7use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
8
9pub trait BinaryIrreducible<const LIMBS: usize>: 'static {
15 fn modulus() -> Uint<LIMBS>;
17
18 fn degree() -> usize;
20}
21
22pub struct F2Ext<const LIMBS: usize, P>
28where
29 P: BinaryIrreducible<LIMBS>,
30{
31 pub value: Uint<LIMBS>,
33 _phantom: PhantomData<P>,
34}
35
36impl<const LIMBS: usize, P> F2Ext<LIMBS, P>
41where
42 P: BinaryIrreducible<LIMBS>,
43{
44 pub fn new(x: Uint<LIMBS>) -> Self {
46 Self {
47 value: reduce::<LIMBS, P>(x),
48 _phantom: PhantomData,
49 }
50 }
51
52 pub fn from_u64(x: u64) -> Self {
54 Self::new(Uint::from(x))
55 }
56
57 pub fn from_uint(x: Uint<LIMBS>) -> Self {
59 Self::new(x)
60 }
61
62 pub fn as_uint(&self) -> Uint<LIMBS> {
64 self.value
65 }
66
67 pub fn degree() -> usize {
69 P::degree()
70 }
71}
72
73impl<const LIMBS: usize, P> Clone for F2Ext<LIMBS, P>
79where
80 P: BinaryIrreducible<LIMBS>,
81{
82 fn clone(&self) -> Self {
83 Self {
84 value: self.value.clone(),
85 _phantom: PhantomData,
86 }
87 }
88}
89
90impl<const LIMBS: usize, P> Copy for F2Ext<LIMBS, P> where P: BinaryIrreducible<LIMBS> {}
91
92impl<const LIMBS: usize, P> PartialEq for F2Ext<LIMBS, P>
93where
94 P: BinaryIrreducible<LIMBS>,
95{
96 fn eq(&self, other: &Self) -> bool {
97 self.value == other.value
98 }
99}
100
101impl<const LIMBS: usize, P> Eq for F2Ext<LIMBS, P> where P: BinaryIrreducible<LIMBS> {}
102
103impl<const LIMBS: usize, P> core::fmt::Debug for F2Ext<LIMBS, P>
104where
105 P: BinaryIrreducible<LIMBS>,
106{
107 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
108 write!(f, "F2Ext({:?})", self.value)
109 }
110}
111
112impl<const LIMBS: usize, P> core::fmt::Display for F2Ext<LIMBS, P>
120where
121 P: BinaryIrreducible<LIMBS>,
122{
123 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
124 let m = P::degree();
125 let mut terms = Vec::new();
126 let words = self.value.to_words();
127
128 for i in (0..m).rev() {
130 let word = words[i / 64];
131 let bit = (word >> (i % 64)) & 1;
132 if bit == 1 {
133 match i {
134 0 => terms.push("1".to_string()),
135 1 => terms.push("x".to_string()),
136 _ => terms.push(format!("x^{i}")),
137 }
138 }
139 }
140
141 if terms.is_empty() {
142 write!(f, "0")
143 } else {
144 write!(f, "{}", terms.join(" + "))
145 }
146 }
147}
148
149
150impl<const LIMBS: usize, P> Default for F2Ext<LIMBS, P>
155where
156 P: BinaryIrreducible<LIMBS>,
157{
158 fn default() -> Self {
159 Self::zero()
160 }
161}
162
163impl<const LIMBS: usize, P> ConditionallySelectable for F2Ext<LIMBS, P>
164where
165 P: BinaryIrreducible<LIMBS>,
166{
167 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
168 Self::new(Uint::<LIMBS>::conditional_select(
169 &a.value, &b.value, choice,
170 ))
171 }
172
173 fn conditional_assign(&mut self, other: &Self, choice: Choice) {
174 self.value.conditional_assign(&other.value, choice)
175 }
176
177 fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) {
178 Uint::<LIMBS>::conditional_swap(&mut a.value, &mut b.value, choice)
179 }
180}
181
182impl<const LIMBS: usize, P> ConstantTimeEq for F2Ext<LIMBS, P>
183where
184 P: BinaryIrreducible<LIMBS>,
185{
186 fn ct_eq(&self, other: &Self) -> Choice {
187 Uint::<LIMBS>::ct_eq(&self.value, &other.value)
188 }
189
190 fn ct_ne(&self, other: &Self) -> Choice {
191 Uint::<LIMBS>::ct_ne(&self.value, &other.value)
192 }
193}
194
195fn reduce<const LIMBS: usize, P>(mut a: Uint<LIMBS>) -> Uint<LIMBS>
200where
201 P: BinaryIrreducible<LIMBS>,
202{
203 let modulus = P::modulus();
204 let m = P::degree();
205 let nbits = Uint::<LIMBS>::BITS as usize;
206
207 debug_assert!(m > 0);
208 debug_assert!(m < nbits);
209
210 for i in (m..nbits).rev() {
212 let bit = a.bit(i as u32); let shifted = modulus << (i - m);
214 let reduced = a ^ shifted;
215
216 a = Uint::<LIMBS>::conditional_select(&a, &reduced, bit.into());
220 }
221 a
222}
223
224fn add_helper<const LIMBS: usize>(a: &Uint<LIMBS>, b: &Uint<LIMBS>) -> Uint<LIMBS> {
225 a ^ b
226}
227
228fn mul_helper<const LIMBS: usize, P>(a: &Uint<LIMBS>, b: &Uint<LIMBS>) -> Uint<LIMBS>
230where
231 P: BinaryIrreducible<LIMBS>,
232{
233 let m = P::degree();
234 let modulus = P::modulus();
235
236 let mut res = Uint::<LIMBS>::ZERO;
237 let mut cur = a.clone(); for i in 0..m {
240 let bit = b.bit(i as u32);
241
242 let res_xor = res ^ cur;
244 res = Uint::<LIMBS>::conditional_select(&res, &res_xor, bit.into());
245
246 let top = cur.bit((m - 1) as u32);
249 let shifted = cur << 1;
252 let reduced = shifted ^ modulus;
256 cur = Uint::<LIMBS>::conditional_select(&shifted, &reduced, top.into());
260 }
261
262 res
263}
264
265fn square_helper<const LIMBS: usize, P>(a: &Uint<LIMBS>) -> Uint<LIMBS>
266where
267 P: BinaryIrreducible<LIMBS>,
268{
269 let m = P::degree();
270 let modulus = P::modulus();
271
272 let mut res = Uint::<LIMBS>::ZERO;
273 let mut cur = Uint::<LIMBS>::ONE; for i in 0..m {
276 let bit = a.bit(i as u32);
277
278 let res_xor = res ^ cur;
280 res = Uint::<LIMBS>::conditional_select(&res, &res_xor, bit.into());
281
282 let top1 = cur.bit((m - 1) as u32);
284 let shifted1 = cur << 1;
285 let reduced1 = shifted1 ^ modulus;
286 cur = Uint::<LIMBS>::conditional_select(&shifted1, &reduced1, top1.into());
287
288 let top2 = cur.bit((m - 1) as u32);
289 let shifted2 = cur << 1;
290 let reduced2 = shifted2 ^ modulus;
291 cur = Uint::<LIMBS>::conditional_select(&shifted2, &reduced2, top2.into());
292 }
293
294 res
295}
296
297fn pow_2k_helper<const LIMBS: usize, P>(a: &Uint<LIMBS>, k: &usize) -> Uint<LIMBS>
298where
299 P: BinaryIrreducible<LIMBS>,
300{
301 let mut res = a.clone();
302 for _ in 0..*k {
303 res = square_helper::<LIMBS, P>(&res);
304 }
305 res
306}
307
308fn itoh_tsujii<const LIMBS: usize, P>(a: &Uint<LIMBS>) -> Uint<LIMBS>
309where
310 P: BinaryIrreducible<LIMBS>,
311{
312 let m = P::degree();
314 if m == 1 {
315 return a.clone();
316 }
317
318 let mut beta = a.clone();
319 let mut r = 1usize;
320
321 let top = (m - 1).ilog2(); for i in (0..top).rev() {
324 let beta_frob = pow_2k_helper::<LIMBS, P>(&beta, &r); beta = mul_helper::<LIMBS, P>(&beta, &beta_frob); r <<= 1; if (((m - 1) >> i) & 1) == 1 {
329 beta = mul_helper::<LIMBS, P>(&square_helper::<LIMBS, P>(&beta), a);
330 r += 1;
332 }
333 }
334 square_helper::<LIMBS, P>(&beta)
335}
336
337fn sqrt_helper<const LIMBS: usize, P>(a: &Uint<LIMBS>) -> Uint<LIMBS>
338where
339 P: BinaryIrreducible<LIMBS>,
340{
341 let m = P::degree();
344 pow_2k_helper::<LIMBS, P>(&a, &(m - 1))
345}
346
347impl<const LIMBS: usize, P> Add for F2Ext<LIMBS, P>
352where
353 P: BinaryIrreducible<LIMBS>,
354{
355 type Output = Self;
356 fn add(self, rhs: Self) -> Self {
357 FieldOps::add(&self, &rhs)
358 }
359}
360
361impl<const LIMBS: usize, P> Sub for F2Ext<LIMBS, P>
362where
363 P: BinaryIrreducible<LIMBS>,
364{
365 type Output = Self;
366 fn sub(self, rhs: Self) -> Self {
367 FieldOps::sub(&self, &rhs)
368 }
369}
370
371impl<const LIMBS: usize, P> Mul for F2Ext<LIMBS, P>
372where
373 P: BinaryIrreducible<LIMBS>,
374{
375 type Output = Self;
376 fn mul(self, rhs: Self) -> Self {
377 FieldOps::mul(&self, &rhs)
378 }
379}
380
381impl<const LIMBS: usize, P> Neg for F2Ext<LIMBS, P>
382where
383 P: BinaryIrreducible<LIMBS>,
384{
385 type Output = Self;
386 fn neg(self) -> Self {
387 FieldOps::negate(&self)
388 }
389}
390
391impl<const LIMBS: usize, P> FieldOps for F2Ext<LIMBS, P>
396where
397 P: BinaryIrreducible<LIMBS>,
398{
399 fn zero() -> Self {
401 Self::from_uint(Uint::<LIMBS>::ZERO)
402 }
403
404 fn one() -> Self {
405 Self::from_uint(Uint::<LIMBS>::ONE)
406 }
407
408 fn from_u64(x: u64) -> Self { Self::from_u64(x) }
409
410 fn is_zero(&self) -> Choice {
411 Self::ct_eq(self, &Self::zero())
412 }
413
414 fn is_one(&self) -> Choice {
415 Self::ct_eq(self, &Self::one())
416 }
417
418 fn negate(&self) -> Self {
419 *self
421 }
422
423 fn add(&self, rhs: &Self) -> Self {
424 Self::new(add_helper(&self.value, &rhs.value))
425 }
426
427 fn sub(&self, rhs: &Self) -> Self {
428 Self::new(add_helper(&self.value, &rhs.value))
429 }
430
431 fn mul(&self, rhs: &Self) -> Self {
432 Self::new(mul_helper::<LIMBS, P>(&self.value, &rhs.value))
433 }
434
435 fn square(&self) -> Self {
436 Self::new(square_helper::<LIMBS, P>(&self.value))
437 }
438
439 fn double(&self) -> Self {
440 Self::zero()
442 }
443
444 fn invert(&self) -> CtOption<Self> {
445 let is_invertible = !self.is_zero();
446 CtOption::new(
447 Self::new(itoh_tsujii::<LIMBS, P>(&self.value)),
448 is_invertible,
449 )
450 }
451
452 fn frobenius(&self) -> Self {
453 self.square()
454 }
455
456 fn trace(&self) -> Self {
457 let deg = P::degree();
458 let mut result = self.clone();
459 let mut conj = self.frobenius();
460 for _ in 1..deg {
461 result = <Self as FieldOps>::add(&result, &conj);
462 conj = conj.frobenius();
463 }
464 result
465 }
466
467 fn norm(&self) -> Self {
468 let deg = P::degree();
469 let mut result = self.clone();
470 let mut conj = self.frobenius();
471 for _ in 1..deg {
472 result = <Self as FieldOps>::mul(&result, &conj);
473 conj = conj.frobenius();
474 }
475 result
476 }
477
478 fn sqrt(&self) -> CtOption<Self> {
479 let sqrt = Self::new(sqrt_helper::<LIMBS, P>(&self.value));
481 CtOption::new(sqrt, Choice::from(1u8))
482 }
483
484 fn legendre(&self) -> i8 {
485 let is_zero = self.is_zero();
486 i8::conditional_select(&0i8, &1i8, is_zero)
487 }
488
489 fn characteristic() -> Vec<u64> {
490 vec![2u64]
491 }
492
493 fn degree() -> u32 {
494 P::degree() as u32
495 }
496
497}
498
499impl<const LIMBS: usize, P> FieldRandom for F2Ext<LIMBS, P>
504where
505 P: BinaryIrreducible<LIMBS>,
506{
507 fn random(rng: &mut (impl rand::CryptoRng + rand::Rng)) -> Self {
512
513 let m = P::degree();
514 let mut words = [0u64; LIMBS];
515 for w in words.iter_mut() {
516 *w = rng.next_u64();
517 }
518
519 let full_limbs = m / 64;
521 let leftover = m % 64;
522
523 for w in words.iter_mut().skip(full_limbs + if leftover > 0 { 1 } else { 0 }) {
525 *w = 0;
526 }
527 if leftover > 0 && full_limbs < LIMBS {
529 words[full_limbs] &= (1u64 << leftover) - 1;
530 }
531
532 Self::new(Uint::from_words(words))
533 }
534}
535
536
537impl<const LIMBS: usize, P> FieldFromRepr for F2Ext<LIMBS, P>
538where
539 P: BinaryIrreducible<LIMBS>,
540{
541 type Repr = Uint<LIMBS>;
542
543 fn from_repr(x: Self::Repr) -> Self {
544 Self::from_uint(x)
545 }
546}