#[cfg(all(target_arch = "aarch64", target_endian = "little", not(miri)))]
mod aarch64;
mod big;
mod mont;
mod p521;
mod pseudo;
mod special;
pub(super) use big::Big;
pub(super) use mont::Mont;
pub(super) use p521::P521;
pub(super) use pseudo::Pseudo;
pub(super) use special::Nist;
use crate::math::numbers::{ByteOrder, Number};
use std::sync::OnceLock;
pub(super) trait Field {
type E: Clone;
const CHEAP_SQUARES: bool = false;
fn limbs(&self) -> usize;
fn load(&self, x: &[u64]) -> Self::E;
fn store(&self, x: &Self::E, r: &mut [u64]);
fn zero(&self) -> Self::E;
fn one(&self) -> Self::E;
fn add(&self, a: &Self::E, b: &Self::E) -> Self::E;
fn sub(&self, a: &Self::E, b: &Self::E) -> Self::E;
fn neg(&self, a: &Self::E) -> Self::E;
fn mul(&self, a: &Self::E, b: &Self::E) -> Self::E;
fn sqr(&self, a: &Self::E) -> Self::E;
fn is_zero(&self, a: &Self::E) -> bool;
fn enter(&self, x: &Number) -> Self::E;
fn leave(&self, x: &Self::E) -> Number;
#[inline(always)]
fn mul_word(&self, a: &Self::E, _w: u64, e: &Self::E) -> Self::E {
self.mul(a, e)
}
#[inline(always)]
fn twice(&self, a: &Self::E) -> Self::E {
self.add(a, a)
}
fn half(&self, a: &Self::E) -> Self::E {
self.mul(a, &self.enter(&((self.modulus() + 1u8) >> 1usize)))
}
#[inline(always)]
fn equal(&self, a: &Self::E, b: &Self::E) -> bool {
self.is_zero(&self.sub(a, b))
}
fn invert(&self, a: &Self::E) -> Option<Self::E> {
let x = self.leave(a);
let p = self.modulus();
x.mod_inverse(p).map(|r| self.enter(&r))
}
fn modulus(&self) -> &Number;
}
pub(super) fn pow<F: Field>(f: &F, x: &F::E, e: &[u64]) -> F::E {
let bit = |i: usize| (e[i / 64] >> (i % 64)) & 1 != 0;
let mut i = bit_length_of(e);
if i == 0 {
return f.one();
}
let run_at = |i: usize| (0..i).take_while(|&j| bit(i - 1 - j)).count();
let top = run_at(i);
let mut ones = vec![x.clone()];
while 2 << (ones.len() - 1) <= top {
let half = ones[ones.len() - 1].clone();
let mut t = half.clone();
for _ in 0..1usize << (ones.len() - 1) {
t = f.sqr(&t);
}
ones.push(f.mul(&t, &half));
}
let pieces = |mut n: usize| {
let mut count = 0;
while n > 0 {
n -= 1 << (usize::BITS - 1 - n.leading_zeros()).min(ones.len() as u32 - 1);
count += 1;
}
count
};
let append = |mut r: F::E, mut n: usize| {
while n > 0 {
let k = (usize::BITS - 1 - n.leading_zeros()).min(ones.len() as u32 - 1) as usize;
for _ in 0..1usize << k {
r = f.sqr(&r);
}
r = f.mul(&r, &ones[k]);
n -= 1 << k;
}
r
};
let first = 1 << (ones.len() - 1);
let mut r = append(ones[ones.len() - 1].clone(), top - first);
i -= top;
let mut table: Vec<F::E> = vec![x.clone()];
while i > 0 {
if !bit(i - 1) {
r = f.sqr(&r);
i -= 1;
continue;
}
let run = run_at(i);
if pieces(run) < run.div_ceil(5) || (ones.len() > 4 && table.len() == 1) {
r = append(r, run);
i -= run;
continue;
}
let mut low = i.saturating_sub(5);
while !bit(low) {
low += 1;
}
let mut d = 0;
for j in (low..i).rev() {
d = 2 * d + usize::from(bit(j));
}
if d > 1 && table.len() == 1 {
let x2 = f.sqr(x);
for k in 1..16 {
let t = f.mul(&table[k - 1], &x2);
table.push(t);
}
}
for _ in low..i {
r = f.sqr(&r);
}
r = f.mul(&r, &table[d / 2]);
i = low;
}
r
}
fn bit_length_of(e: &[u64]) -> usize {
match e.iter().rposition(|&l| l != 0) {
Some(i) => 64 * i + 64 - e[i].leading_zeros() as usize,
None => 0,
}
}
pub(super) fn invert_all<F: Field>(f: &F, x: &mut [F::E]) {
let mut running = Vec::with_capacity(x.len());
let mut product = f.one();
for e in x.iter() {
running.push(product.clone());
if !f.is_zero(e) {
product = f.mul(&product, e);
}
}
let Some(mut inverse) = f.invert(&product) else {
return;
};
for (e, before) in x.iter_mut().zip(running.iter()).rev() {
if !f.is_zero(e) {
let i = f.mul(&inverse, before);
inverse = f.mul(&inverse, e);
*e = i;
}
}
}
pub struct SqrtPlan {
s: u32,
half: Vec<u64>,
c: Vec<u64>,
}
pub(super) fn sqrt<F: Field>(f: &F, plan: &SqrtPlan, a: &F::E) -> Option<F::E> {
if f.is_zero(a) {
return Some(f.zero());
}
let x = pow(f, a, &plan.half);
let mut r = f.mul(a, &x);
let mut b = f.mul(&r, &x);
let one = f.one();
if plan.s == 1 {
return f.equal(&b, &one).then_some(r);
}
let mut c = f.load(&plan.c);
let mut m = plan.s;
while !f.equal(&b, &one) {
let mut t = 0;
let mut b2 = b.clone();
while !f.equal(&b2, &one) {
b2 = f.sqr(&b2);
t += 1;
if t == m {
return None;
}
}
let mut g = c;
for _ in 0..m - t - 1 {
g = f.sqr(&g);
}
r = f.mul(&r, &g);
c = f.sqr(&g);
b = f.mul(&b, &c);
m = t;
}
Some(r)
}
pub(super) fn sqrt_ratio<F: Field>(f: &F, plan: &SqrtPlan, u: &F::E, v: &F::E) -> Option<F::E> {
if plan.s > 2 {
return sqrt(f, plan, &f.mul(u, &f.invert(v)?));
}
let v2 = f.sqr(v);
let v3 = f.mul(&v2, v);
let (front, inner) = if plan.s == 2 {
let uv3 = f.mul(u, &v3);
let uv7 = f.mul(&uv3, &f.sqr(&v2));
(uv3, uv7)
} else {
(f.mul(u, v), f.mul(u, &v3))
};
let x = f.mul(&front, &pow(f, &inner, &plan.half));
let vxx = f.mul(v, &f.sqr(&x));
if f.equal(&vxx, u) {
return Some(x);
}
(plan.s == 2 && f.equal(&vxx, &f.neg(u))).then(|| f.mul(&x, &f.load(&plan.c)))
}
#[allow(clippy::large_enum_variant)]
pub enum Prime {
Mont1(Mont<1>),
Mont2(Mont<2>),
Mont3(Mont<3>),
Mont4(Mont<4>),
Mont5(Mont<5>),
Mont6(Mont<6>),
Mont7(Mont<7>),
Mont8(Mont<8>),
Mont9(Mont<9>),
Pseudo4(Pseudo<4>),
Pseudo6(Pseudo<6>),
Pseudo9(Pseudo<9>),
P256(Nist<4>),
P384(Nist<6>),
P521(P521),
Big(Big),
}
macro_rules! with_prime {
($prime:expr, $f:ident => $body:expr) => {
match $prime {
$crate::math::ec::field::Prime::Mont1($f) => $body,
$crate::math::ec::field::Prime::Mont2($f) => $body,
$crate::math::ec::field::Prime::Mont3($f) => $body,
$crate::math::ec::field::Prime::Mont4($f) => $body,
$crate::math::ec::field::Prime::Mont5($f) => $body,
$crate::math::ec::field::Prime::Mont6($f) => $body,
$crate::math::ec::field::Prime::Mont7($f) => $body,
$crate::math::ec::field::Prime::Mont8($f) => $body,
$crate::math::ec::field::Prime::Mont9($f) => $body,
$crate::math::ec::field::Prime::Pseudo4($f) => $body,
$crate::math::ec::field::Prime::Pseudo6($f) => $body,
$crate::math::ec::field::Prime::Pseudo9($f) => $body,
$crate::math::ec::field::Prime::P256($f) => $body,
$crate::math::ec::field::Prime::P384($f) => $body,
$crate::math::ec::field::Prime::P521($f) => $body,
$crate::math::ec::field::Prime::Big($f) => $body,
}
};
}
pub(super) use with_prime;
const P256_LIMBS: [u64; 4] = [u64::MAX, 0xffff_ffff, 0, 0xffff_ffff_0000_0001];
const P384_LIMBS: [u64; 6] = [
0xffff_ffff,
0xffff_ffff_0000_0000,
u64::MAX - 1,
u64::MAX,
u64::MAX,
u64::MAX,
];
const P521_LIMBS: [u64; 9] = [
u64::MAX,
u64::MAX,
u64::MAX,
u64::MAX,
u64::MAX,
u64::MAX,
u64::MAX,
u64::MAX,
0x1ff,
];
impl Prime {
fn new(p: &Number) -> Prime {
let l = limbs_of(p);
if l == P256_LIMBS {
return Prime::P256(Nist::new(Mont::new(p, P256_LIMBS)));
}
if l == P384_LIMBS {
return Prime::P384(Nist::new(Mont::new(p, P384_LIMBS)));
}
if l == P521_LIMBS {
return Prime::P521(P521::new(p));
}
macro_rules! pseudo_or_mont {
($($n:literal, $mont:ident, $pseudo:ident);+ $(;)?) => {
match l.len() {
$($n => {
let m: [u64; $n] = l[..].try_into().unwrap_or([0; $n]);
match Pseudo::<$n>::new(p) {
Some(f) => Prime::$pseudo(f),
None => Prime::$mont(Mont::new(p, m)),
}
})+
1 => Prime::Mont1(Mont::new(p, [l[0]])),
2 => Prime::Mont2(Mont::new(p, [l[0], l[1]])),
3 => Prime::Mont3(Mont::new(p, [l[0], l[1], l[2]])),
5 => Prime::Mont5(Mont::new(p, [l[0], l[1], l[2], l[3], l[4]])),
7 => Prime::Mont7(Mont::new(p, [l[0], l[1], l[2], l[3], l[4], l[5], l[6]])),
8 => Prime::Mont8(Mont::new(
p,
[l[0], l[1], l[2], l[3], l[4], l[5], l[6], l[7]],
)),
_ => Prime::Big(Big::new(p)),
}
};
}
pseudo_or_mont!(4, Mont4, Pseudo4; 6, Mont6, Pseudo6; 9, Mont9, Pseudo9)
}
}
pub struct Base {
pub(super) prime: Prime,
pub(super) p: Number,
sqrt: OnceLock<SqrtPlan>,
}
impl Base {
pub(super) fn new(p: &Number) -> Base {
Base {
prime: Prime::new(p),
p: p.clone(),
sqrt: OnceLock::new(),
}
}
pub(super) fn sqrt_plan(&self) -> &SqrtPlan {
self.sqrt.get_or_init(|| {
let p = &self.p;
let one = Number::new_from_u8(1, p.byte_order);
let p1 = p - &one;
let s = p1.trailing_zeros().unwrap_or(0);
let q = &p1 >> s as usize;
let half = (&q - &one) >> 1usize;
let mut z = Number::new_from_u8(2, p.byte_order);
while s > 1 && z.jacobi(p) != -1 {
z = &z + &one;
}
let c = z.pow_mod(&q, p);
SqrtPlan {
s: s as u32,
half: limbs_of(&half),
c: with_prime!(&self.prime, f => {
let mut r = vec![0; f.limbs()];
f.store(&f.enter(&c), &mut r);
r
}),
}
})
}
pub(super) fn bytes(&self) -> usize {
self.p.bits().div_ceil(8) as usize
}
}
pub(super) fn limbs_of(x: &Number) -> Vec<u64> {
let mut l: Vec<u64> = if x.byte_order == ByteOrder::LittleEndian {
x.value
.chunks(8)
.map(|c| {
let mut b = [0; 8];
b[..c.len()].copy_from_slice(c);
u64::from_le_bytes(b)
})
.collect()
} else {
x.value
.rchunks(8)
.map(|c| {
let mut b = [0; 8];
b[8 - c.len()..].copy_from_slice(c);
u64::from_be_bytes(b)
})
.collect()
};
while l.last() == Some(&0) {
l.pop();
}
l
}
pub(super) fn reduced(x: &Number, p: &Number) -> Number {
let mut r = x.rem_euclid(p);
if r.byte_order != p.byte_order {
r.value.reverse();
r.byte_order = p.byte_order;
}
r
}
pub(super) fn number_of(x: &[u64], order: ByteOrder) -> Number {
let mut bytes = Vec::with_capacity(8 * x.len());
for l in x {
bytes.extend_from_slice(&l.to_le_bytes());
}
let mut n = Number::new(bytes, ByteOrder::LittleEndian);
if order == ByteOrder::BigEndian {
n.value.reverse();
n.byte_order = ByteOrder::BigEndian;
}
n
}
#[inline(always)]
fn halve<const N: usize>(a: &[u64; N], m: &[u64; N]) -> [u64; N] {
let mask = (a[0] & 1).wrapping_neg();
let mut s = [0; N];
let mut carry = false;
for i in 0..N {
let (x, c1) = a[i].overflowing_add(m[i] & mask);
let (x, c2) = x.overflowing_add(u64::from(carry));
s[i] = x;
carry = c1 | c2;
}
let mut r = [0; N];
for i in 0..N {
let above = if i + 1 < N {
s[i + 1]
} else {
u64::from(carry)
};
r[i] = (s[i] >> 1) | (above << 63);
}
r
}
#[cfg(all(target_arch = "aarch64", target_endian = "little", not(miri)))]
#[inline(always)]
fn same<const N: usize, const M: usize>(x: &[u64; N]) -> &[u64; M] {
x.as_slice().try_into().unwrap_or_else(|_| unreachable!())
}
pub(super) fn array_of<const N: usize>(x: &Number) -> [u64; N] {
let l = limbs_of(x);
let mut r = [0; N];
r[..l.len()].copy_from_slice(&l);
r
}