use crate::field_ops::{FieldFromRepr, FieldOps, FieldRandom};
use core::ops::{Add, Mul, Neg, Sub};
use crypto_bigint::Uint;
use std::marker::PhantomData;
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
pub trait BinaryIrreducible<const LIMBS: usize>: 'static {
fn modulus() -> Uint<LIMBS>;
fn degree() -> usize;
}
pub struct F2Ext<const LIMBS: usize, P>
where
P: BinaryIrreducible<LIMBS>,
{
pub value: Uint<LIMBS>,
_phantom: PhantomData<P>,
}
impl<const LIMBS: usize, P> F2Ext<LIMBS, P>
where
P: BinaryIrreducible<LIMBS>,
{
pub fn new(x: Uint<LIMBS>) -> Self {
Self {
value: reduce::<LIMBS, P>(x),
_phantom: PhantomData,
}
}
pub fn from_u64(x: u64) -> Self {
Self::new(Uint::from(x))
}
pub fn from_uint(x: Uint<LIMBS>) -> Self {
Self::new(x)
}
pub fn as_uint(&self) -> Uint<LIMBS> {
self.value
}
pub fn degree() -> usize {
P::degree()
}
}
impl<const LIMBS: usize, P> Clone for F2Ext<LIMBS, P>
where
P: BinaryIrreducible<LIMBS>,
{
fn clone(&self) -> Self {
Self {
value: self.value.clone(),
_phantom: PhantomData,
}
}
}
impl<const LIMBS: usize, P> Copy for F2Ext<LIMBS, P> where P: BinaryIrreducible<LIMBS> {}
impl<const LIMBS: usize, P> PartialEq for F2Ext<LIMBS, P>
where
P: BinaryIrreducible<LIMBS>,
{
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
impl<const LIMBS: usize, P> Eq for F2Ext<LIMBS, P> where P: BinaryIrreducible<LIMBS> {}
impl<const LIMBS: usize, P> core::fmt::Debug for F2Ext<LIMBS, P>
where
P: BinaryIrreducible<LIMBS>,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "F2Ext({:?})", self.value)
}
}
impl<const LIMBS: usize, P> core::fmt::Display for F2Ext<LIMBS, P>
where
P: BinaryIrreducible<LIMBS>,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let m = P::degree();
let mut terms = Vec::new();
let words = self.value.to_words();
for i in (0..m).rev() {
let word = words[i / 64];
let bit = (word >> (i % 64)) & 1;
if bit == 1 {
match i {
0 => terms.push("1".to_string()),
1 => terms.push("x".to_string()),
_ => terms.push(format!("x^{i}")),
}
}
}
if terms.is_empty() {
write!(f, "0")
} else {
write!(f, "{}", terms.join(" + "))
}
}
}
impl<const LIMBS: usize, P> Default for F2Ext<LIMBS, P>
where
P: BinaryIrreducible<LIMBS>,
{
fn default() -> Self {
Self::zero()
}
}
impl<const LIMBS: usize, P> ConditionallySelectable for F2Ext<LIMBS, P>
where
P: BinaryIrreducible<LIMBS>,
{
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
Self::new(Uint::<LIMBS>::conditional_select(
&a.value, &b.value, choice,
))
}
fn conditional_assign(&mut self, other: &Self, choice: Choice) {
self.value.conditional_assign(&other.value, choice)
}
fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) {
Uint::<LIMBS>::conditional_swap(&mut a.value, &mut b.value, choice)
}
}
impl<const LIMBS: usize, P> ConstantTimeEq for F2Ext<LIMBS, P>
where
P: BinaryIrreducible<LIMBS>,
{
fn ct_eq(&self, other: &Self) -> Choice {
Uint::<LIMBS>::ct_eq(&self.value, &other.value)
}
fn ct_ne(&self, other: &Self) -> Choice {
Uint::<LIMBS>::ct_ne(&self.value, &other.value)
}
}
fn reduce<const LIMBS: usize, P>(mut a: Uint<LIMBS>) -> Uint<LIMBS>
where
P: BinaryIrreducible<LIMBS>,
{
let modulus = P::modulus();
let m = P::degree();
let nbits = Uint::<LIMBS>::BITS as usize;
debug_assert!(m > 0);
debug_assert!(m < nbits);
for i in (m..nbits).rev() {
let bit = a.bit(i as u32); let shifted = modulus << (i - m);
let reduced = a ^ shifted;
a = Uint::<LIMBS>::conditional_select(&a, &reduced, bit.into());
}
a
}
fn add_helper<const LIMBS: usize>(a: &Uint<LIMBS>, b: &Uint<LIMBS>) -> Uint<LIMBS> {
a ^ b
}
fn mul_helper<const LIMBS: usize, P>(a: &Uint<LIMBS>, b: &Uint<LIMBS>) -> Uint<LIMBS>
where
P: BinaryIrreducible<LIMBS>,
{
let m = P::degree();
let modulus = P::modulus();
let mut res = Uint::<LIMBS>::ZERO;
let mut cur = a.clone();
for i in 0..m {
let bit = b.bit(i as u32);
let res_xor = res ^ cur;
res = Uint::<LIMBS>::conditional_select(&res, &res_xor, bit.into());
let top = cur.bit((m - 1) as u32);
let shifted = cur << 1;
let reduced = shifted ^ modulus;
cur = Uint::<LIMBS>::conditional_select(&shifted, &reduced, top.into());
}
res
}
fn square_helper<const LIMBS: usize, P>(a: &Uint<LIMBS>) -> Uint<LIMBS>
where
P: BinaryIrreducible<LIMBS>,
{
let m = P::degree();
let modulus = P::modulus();
let mut res = Uint::<LIMBS>::ZERO;
let mut cur = Uint::<LIMBS>::ONE;
for i in 0..m {
let bit = a.bit(i as u32);
let res_xor = res ^ cur;
res = Uint::<LIMBS>::conditional_select(&res, &res_xor, bit.into());
let top1 = cur.bit((m - 1) as u32);
let shifted1 = cur << 1;
let reduced1 = shifted1 ^ modulus;
cur = Uint::<LIMBS>::conditional_select(&shifted1, &reduced1, top1.into());
let top2 = cur.bit((m - 1) as u32);
let shifted2 = cur << 1;
let reduced2 = shifted2 ^ modulus;
cur = Uint::<LIMBS>::conditional_select(&shifted2, &reduced2, top2.into());
}
res
}
fn pow_2k_helper<const LIMBS: usize, P>(a: &Uint<LIMBS>, k: &usize) -> Uint<LIMBS>
where
P: BinaryIrreducible<LIMBS>,
{
let mut res = a.clone();
for _ in 0..*k {
res = square_helper::<LIMBS, P>(&res);
}
res
}
fn itoh_tsujii<const LIMBS: usize, P>(a: &Uint<LIMBS>) -> Uint<LIMBS>
where
P: BinaryIrreducible<LIMBS>,
{
let m = P::degree();
if m == 1 {
return a.clone();
}
let mut beta = a.clone();
let mut r = 1usize;
let top = (m - 1).ilog2();
for i in (0..top).rev() {
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 {
beta = mul_helper::<LIMBS, P>(&square_helper::<LIMBS, P>(&beta), a);
r += 1;
}
}
square_helper::<LIMBS, P>(&beta)
}
fn sqrt_helper<const LIMBS: usize, P>(a: &Uint<LIMBS>) -> Uint<LIMBS>
where
P: BinaryIrreducible<LIMBS>,
{
let m = P::degree();
pow_2k_helper::<LIMBS, P>(&a, &(m - 1))
}
impl<const LIMBS: usize, P> Add for F2Ext<LIMBS, P>
where
P: BinaryIrreducible<LIMBS>,
{
type Output = Self;
fn add(self, rhs: Self) -> Self {
FieldOps::add(&self, &rhs)
}
}
impl<const LIMBS: usize, P> Sub for F2Ext<LIMBS, P>
where
P: BinaryIrreducible<LIMBS>,
{
type Output = Self;
fn sub(self, rhs: Self) -> Self {
FieldOps::sub(&self, &rhs)
}
}
impl<const LIMBS: usize, P> Mul for F2Ext<LIMBS, P>
where
P: BinaryIrreducible<LIMBS>,
{
type Output = Self;
fn mul(self, rhs: Self) -> Self {
FieldOps::mul(&self, &rhs)
}
}
impl<const LIMBS: usize, P> Neg for F2Ext<LIMBS, P>
where
P: BinaryIrreducible<LIMBS>,
{
type Output = Self;
fn neg(self) -> Self {
FieldOps::negate(&self)
}
}
impl<const LIMBS: usize, P> FieldOps for F2Ext<LIMBS, P>
where
P: BinaryIrreducible<LIMBS>,
{
fn zero() -> Self {
Self::from_uint(Uint::<LIMBS>::ZERO)
}
fn one() -> Self {
Self::from_uint(Uint::<LIMBS>::ONE)
}
fn from_u64(x: u64) -> Self { Self::from_u64(x) }
fn is_zero(&self) -> Choice {
Self::ct_eq(self, &Self::zero())
}
fn is_one(&self) -> Choice {
Self::ct_eq(self, &Self::one())
}
fn negate(&self) -> Self {
*self
}
fn add(&self, rhs: &Self) -> Self {
Self::new(add_helper(&self.value, &rhs.value))
}
fn sub(&self, rhs: &Self) -> Self {
Self::new(add_helper(&self.value, &rhs.value))
}
fn mul(&self, rhs: &Self) -> Self {
Self::new(mul_helper::<LIMBS, P>(&self.value, &rhs.value))
}
fn square(&self) -> Self {
Self::new(square_helper::<LIMBS, P>(&self.value))
}
fn double(&self) -> Self {
Self::zero()
}
fn invert(&self) -> CtOption<Self> {
let is_invertible = !self.is_zero();
CtOption::new(
Self::new(itoh_tsujii::<LIMBS, P>(&self.value)),
is_invertible,
)
}
fn frobenius(&self) -> Self {
self.square()
}
fn trace(&self) -> Self {
let deg = P::degree();
let mut result = self.clone();
let mut conj = self.frobenius();
for _ in 1..deg {
result = <Self as FieldOps>::add(&result, &conj);
conj = conj.frobenius();
}
result
}
fn norm(&self) -> Self {
let deg = P::degree();
let mut result = self.clone();
let mut conj = self.frobenius();
for _ in 1..deg {
result = <Self as FieldOps>::mul(&result, &conj);
conj = conj.frobenius();
}
result
}
fn sqrt(&self) -> CtOption<Self> {
let sqrt = Self::new(sqrt_helper::<LIMBS, P>(&self.value));
CtOption::new(sqrt, Choice::from(1u8))
}
fn legendre(&self) -> i8 {
let is_zero = self.is_zero();
i8::conditional_select(&0i8, &1i8, is_zero)
}
fn characteristic() -> Vec<u64> {
vec![2u64]
}
fn degree() -> u32 {
P::degree() as u32
}
}
impl<const LIMBS: usize, P> FieldRandom for F2Ext<LIMBS, P>
where
P: BinaryIrreducible<LIMBS>,
{
fn random(rng: &mut (impl rand::CryptoRng + rand::Rng)) -> Self {
let m = P::degree();
let mut words = [0u64; LIMBS];
for w in words.iter_mut() {
*w = rng.next_u64();
}
let full_limbs = m / 64;
let leftover = m % 64;
for w in words.iter_mut().skip(full_limbs + if leftover > 0 { 1 } else { 0 }) {
*w = 0;
}
if leftover > 0 && full_limbs < LIMBS {
words[full_limbs] &= (1u64 << leftover) - 1;
}
Self::new(Uint::from_words(words))
}
}
impl<const LIMBS: usize, P> FieldFromRepr for F2Ext<LIMBS, P>
where
P: BinaryIrreducible<LIMBS>,
{
type Repr = Uint<LIMBS>;
fn from_repr(x: Self::Repr) -> Self {
Self::from_uint(x)
}
}