use crate::bignum::{MontModulus, Uint};
use crate::ct::{Choice, ConditionallySelectable, ConstantTimeEq};
pub(crate) type Fe = Uint<4>;
const P_HEX: &str = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed";
const D_HEX: &str = "52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3";
const L_HEX: &str = "1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed";
pub(crate) const BASE_ENC: [u8; 32] = {
let mut b = [0x66u8; 32];
b[0] = 0x58;
b
};
fn fe_from_be_hex(hex: &str) -> Fe {
let h = hex.as_bytes();
let mut bytes = [0u8; 32];
let mut i = 0;
while i < 32 {
let hi = (h[2 * i] as char).to_digit(16).unwrap() as u8;
let lo = (h[2 * i + 1] as char).to_digit(16).unwrap() as u8;
bytes[i] = (hi << 4) | lo;
i += 1;
}
Fe::from_be_bytes(&bytes)
}
fn fe_pow(fp: &MontModulus<4>, one: &Fe, base: Fe, exp: &Fe) -> Fe {
let mut r = *one;
let limbs = exp.as_limbs();
let mut i = 256;
while i > 0 {
i -= 1;
r = fp.mont_mul(&r, &r);
let bit = ((limbs[i / 64] >> (i % 64)) & 1) as u8;
let prod = fp.mont_mul(&r, &base);
r = Fe::conditional_select(&prod, &r, Choice::from(bit));
}
r
}
pub(crate) struct Field {
fp: MontModulus<4>,
pub(crate) one: Fe,
pub(crate) d: Fe,
pub(crate) d2: Fe,
pub(crate) sqrtm1: Fe,
p_minus_2: Fe,
p_minus_5_div_8: Fe,
pub(crate) p: Fe,
pub(crate) l: Fe,
pub(crate) l8: Uint<8>,
}
impl Field {
pub(crate) fn new() -> Self {
let p = fe_from_be_hex(P_HEX);
let fp = MontModulus::new(p);
let one = fp.to_mont(&Fe::ONE);
let d = fp.to_mont(&fe_from_be_hex(D_HEX));
let d2 = fp.add_mod(&d, &d);
let p_minus_2 = p.wrapping_sub(&Fe::from_u64(2));
let p_minus_5_div_8 = p.wrapping_sub(&Fe::from_u64(5)).shr1().shr1().shr1();
let p_minus_1_div_4 = p.wrapping_sub(&Fe::ONE).shr1().shr1();
let two = fp.to_mont(&Fe::from_u64(2));
let sqrtm1 = fe_pow(&fp, &one, two, &p_minus_1_div_4);
let l = fe_from_be_hex(L_HEX);
let ll = l.as_limbs();
let l8 = Uint::<8>::from_limbs([ll[0], ll[1], ll[2], ll[3], 0, 0, 0, 0]);
Field {
fp,
one,
d,
d2,
sqrtm1,
p_minus_2,
p_minus_5_div_8,
p,
l,
l8,
}
}
#[inline]
pub(crate) fn mul(&self, a: Fe, b: Fe) -> Fe {
self.fp.mont_mul(&a, &b)
}
#[inline]
pub(crate) fn sq(&self, a: Fe) -> Fe {
self.fp.mont_mul(&a, &a)
}
#[inline]
pub(crate) fn add(&self, a: Fe, b: Fe) -> Fe {
self.fp.add_mod(&a, &b)
}
#[inline]
pub(crate) fn sub(&self, a: Fe, b: Fe) -> Fe {
self.fp.sub_mod(&a, &b)
}
#[inline]
pub(crate) fn neg(&self, a: Fe) -> Fe {
self.fp.sub_mod(&Fe::ZERO, &a)
}
#[inline]
pub(crate) fn inv(&self, a: Fe) -> Fe {
fe_pow(&self.fp, &self.one, a, &self.p_minus_2)
}
#[inline]
pub(crate) fn to_mont(&self, x: &Fe) -> Fe {
self.fp.to_mont(x)
}
#[inline]
#[allow(clippy::wrong_self_convention)]
pub(crate) fn from_mont(&self, x: &Fe) -> Fe {
self.fp.from_mont(x)
}
#[cfg(feature = "ristretto255")]
#[inline]
pub(crate) fn is_zero(&self, a: Fe) -> Choice {
self.fp.from_mont(&a).ct_eq(&Fe::ZERO)
}
#[inline]
pub(crate) fn is_negative(&self, a: Fe) -> Choice {
Choice::from(self.fp.from_mont(&a).is_odd().unwrap_u8())
}
#[inline]
pub(crate) fn ct_eq(&self, a: Fe, b: Fe) -> Choice {
a.ct_eq(&b)
}
#[inline]
pub(crate) fn conditional_negate(&self, a: Fe, c: Choice) -> Fe {
Fe::conditional_select(&self.neg(a), &a, c)
}
#[inline]
pub(crate) fn pow(&self, base: Fe, exp: &Fe) -> Fe {
fe_pow(&self.fp, &self.one, base, exp)
}
pub(crate) fn sqrt_ratio_i(&self, u: Fe, v: Fe) -> (Choice, Fe) {
let v3 = self.mul(self.sq(v), v);
let v7 = self.mul(self.sq(v3), v);
let pw = self.pow(self.mul(u, v7), &self.p_minus_5_div_8);
let r = self.mul(self.mul(u, v3), pw);
let check = self.mul(v, self.sq(r));
let neg_u = self.neg(u);
let i_u = self.mul(self.sqrtm1, u);
let neg_i_u = self.neg(i_u);
let correct_sign = self.ct_eq(check, u);
let flipped_sign = self.ct_eq(check, neg_u);
let flipped_sign_i = self.ct_eq(check, neg_i_u);
let r_prime = self.mul(self.sqrtm1, r);
let use_r_prime = flipped_sign | flipped_sign_i;
let r = Fe::conditional_select(&r_prime, &r, use_r_prime);
let r = self.abs(r);
let was_square = correct_sign | flipped_sign;
(was_square, r)
}
#[inline]
pub(crate) fn abs(&self, a: Fe) -> Fe {
self.conditional_negate(a, self.is_negative(a))
}
}