use crate::bignum::{MontModulus, Uint};
use crate::ct::{Choice, ConditionallySelectable, ConstantTimeEq};
pub(crate) type Fe = Uint<7>;
const P_HEX: &str = "fffffffffffffffffffffffffffffffffffffffffffffffffffffffe\
ffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
const D_HEX: &str = "fffffffffffffffffffffffffffffffffffffffffffffffffffffffe\
ffffffffffffffffffffffffffffffffffffffffffffffffffff6756";
const L_HEX: &str = "3fffffffffffffffffffffffffffffffffffffffffffffffffffffff\
7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3";
pub(crate) const BASE_ENC: [u8; 57] = {
let mut b = [0u8; 57];
let yle: [u8; 56] = [
0x14, 0xfa, 0x30, 0xf2, 0x5b, 0x79, 0x08, 0x98, 0xad, 0xc8, 0xd7, 0x4e, 0x2c, 0x13, 0xbd,
0xfd, 0xc4, 0x39, 0x7c, 0xe6, 0x1c, 0xff, 0xd3, 0x3a, 0xd7, 0xc2, 0xa0, 0x05, 0x1e, 0x9c,
0x78, 0x87, 0x40, 0x98, 0xa3, 0x6c, 0x73, 0x73, 0xea, 0x4b, 0x62, 0xc7, 0xc9, 0x56, 0x37,
0x20, 0x76, 0x88, 0x24, 0xbc, 0xb6, 0x6e, 0x71, 0x46, 0x3f, 0x69,
];
let mut i = 0;
while i < 56 {
b[i] = yle[i];
i += 1;
}
b
};
fn fe_from_be_hex(hex: &str) -> Fe {
let h = hex.as_bytes();
let mut bytes = [0u8; 56];
let mut i = 0;
while i < 56 {
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<7>, one: &Fe, base: Fe, exp: &Fe) -> Fe {
let mut r = *one;
let limbs = exp.as_limbs();
let mut i = 448;
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<7>,
pub(crate) one: Fe,
pub(crate) d: Fe,
p_minus_2: Fe,
p_minus_3_div_4: Fe,
pub(crate) p: Fe,
pub(crate) l: Fe,
pub(crate) l15: Uint<15>,
}
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 p_minus_2 = p.wrapping_sub(&Fe::from_u64(2));
let p_minus_3_div_4 = p.wrapping_sub(&Fe::from_u64(3)).shr1().shr1();
let l = fe_from_be_hex(L_HEX);
let ll = l.as_limbs();
let l15 = Uint::<15>::from_limbs([
ll[0], ll[1], ll[2], ll[3], ll[4], ll[5], ll[6], 0, 0, 0, 0, 0, 0, 0, 0,
]);
Field {
fp,
one,
d,
p_minus_2,
p_minus_3_div_4,
p,
l,
l15,
}
}
#[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)
}
#[inline]
pub(crate) fn ct_eq(&self, a: Fe, b: Fe) -> Choice {
a.ct_eq(&b)
}
#[inline]
pub(crate) fn pow(&self, base: Fe, exp: &Fe) -> Fe {
fe_pow(&self.fp, &self.one, base, exp)
}
pub(crate) fn sqrt_ratio(&self, u: Fe, v: Fe) -> (Choice, Fe) {
let v2 = self.sq(v);
let v3 = self.mul(v2, v);
let uv3 = self.mul(u, v3);
let pw = self.pow(uv3, &self.p_minus_3_div_4);
let r = self.mul(self.mul(u, v), pw);
let check = self.mul(v, self.sq(r));
let is_square = self.ct_eq(check, u);
(is_square, r)
}
}