use super::point::{xdbl_a24, xdbl_e0};
use super::{EcBasis, EcCurve, EcPoint, JacPoint};
use crate::fp::{Fp, Fp2, FpBackend};
use subtle::Choice;
#[inline]
pub fn ec_recover_y<L: FpBackend>(px: &Fp2<L>, curve: &EcCurve<L>) -> (Fp2<L>, Choice) {
let t0 = px.sqr();
let y = t0.mul(&curve.a); let y = y.add(px); let t0 = t0.mul(px);
let mut y = y.add(&t0); let valid = y.sqrt_verify();
(y, valid)
}
#[inline]
pub fn difference_point<L: FpBackend>(
p: &EcPoint<L>,
q: &EcPoint<L>,
curve: &EcCurve<L>,
) -> EcPoint<L> {
let t0 = p.x.mul(&q.x);
let t1 = p.z.mul(&q.z);
let bxx = t0.sub(&t1);
let bxx = bxx.sqr();
let bxx = bxx.mul(&curve.c);
let bxz = t0.add(&t1);
let t0 = p.x.mul(&q.z);
let t1 = p.z.mul(&q.x);
let bzz_sum = t0.add(&t1);
let bxz = bxz.mul(&bzz_sum);
let bzz = t0.sub(&t1);
let bzz = bzz.sqr();
let bzz = bzz.mul(&curve.c);
let bxz = bxz.mul(&curve.c); let t0_t1 = t0.mul(&t1);
let two_a_pzqz = t0_t1.mul(&curve.a);
let two_a_pzqz = two_a_pzqz.add(&two_a_pzqz);
let bxz = bxz.add(&two_a_pzqz);
let norm = curve.c.conjugate();
let norm = norm.sqr();
let norm = norm.mul(&curve.c);
let pz_bar = p.z.conjugate();
let pz_bar_sq = pz_bar.sqr();
let norm = norm.mul(&pz_bar_sq);
let qz_bar = q.z.conjugate();
let qz_bar_sq = qz_bar.sqr();
let norm = norm.mul(&qz_bar_sq);
let bxx = bxx.mul(&norm);
let bxz = bxz.mul(&norm);
let bzz = bzz.mul(&norm);
let disc = bxz.sqr();
let prod = bxx.mul(&bzz);
let disc = disc.sub(&prod);
let disc = disc.sqrt();
let pq_x = bxz.add(&disc);
let pq_z = bzz;
EcPoint::new(pq_x, pq_z)
}
#[inline]
pub fn difference_point_with_hint<L: FpBackend>(
p: &EcPoint<L>,
q: &EcPoint<L>,
curve: &EcCurve<L>,
negate_disc: bool,
) -> Option<EcPoint<L>> {
let t0 = p.x.mul(&q.x);
let t1 = p.z.mul(&q.z);
let bxx = t0.sub(&t1);
let bxx = bxx.sqr();
let bxx = bxx.mul(&curve.c);
let bxz = t0.add(&t1);
let t0 = p.x.mul(&q.z);
let t1 = p.z.mul(&q.x);
let bzz_sum = t0.add(&t1);
let bxz = bxz.mul(&bzz_sum);
let bzz = t0.sub(&t1);
let bzz = bzz.sqr();
let bzz = bzz.mul(&curve.c);
let bxz = bxz.mul(&curve.c);
let t0_t1 = t0.mul(&t1);
let two_a_pzqz = t0_t1.mul(&curve.a);
let two_a_pzqz = two_a_pzqz.add(&two_a_pzqz);
let bxz = bxz.add(&two_a_pzqz);
let norm = curve.c.conjugate();
let norm = norm.sqr();
let norm = norm.mul(&curve.c);
let pz_bar = p.z.conjugate();
let pz_bar_sq = pz_bar.sqr();
let norm = norm.mul(&pz_bar_sq);
let qz_bar = q.z.conjugate();
let qz_bar_sq = qz_bar.sqr();
let norm = norm.mul(&qz_bar_sq);
let bxx = bxx.mul(&norm);
let bxz = bxz.mul(&norm);
let bzz = bzz.mul(&norm);
let disc = bxz.sqr();
let prod = bxx.mul(&bzz);
let disc = disc.sub(&prod);
let disc = disc.sqrt();
if negate_disc && bool::from(disc.ct_is_zero()) {
return None;
}
let pq_x = if negate_disc {
bxz.sub(&disc)
} else {
bxz.add(&disc)
};
Some(EcPoint::new(pq_x, bzz))
}
#[inline]
pub fn lift_basis_normalized<L: FpBackend>(
basis: &mut EcBasis<L>,
curve: &EcCurve<L>,
) -> (JacPoint<L>, JacPoint<L>, Choice) {
let (py, ret) = ec_recover_y(&basis.p.x, curve);
let jp = JacPoint::new(basis.p.x.clone(), py.clone(), Fp2::one());
let v1 = jp.x.mul(&basis.q.z);
let v2 = basis.q.x.add(&v1);
let v3 = basis.q.x.sub(&v1);
let v3 = v3.sqr();
let v3 = v3.mul(&basis.pmq.x);
let v1_2a = curve.a.add(&curve.a);
let v1_2a = v1_2a.mul(&basis.q.z);
let v2 = v2.add(&v1_2a);
let v4 = jp.x.mul(&basis.q.x);
let v4 = v4.add(&basis.q.z);
let v2 = v2.mul(&v4);
let v1_sub = v1_2a.mul(&basis.q.z);
let v2 = v2.sub(&v1_sub);
let v2 = v2.mul(&basis.pmq.z);
let qy_num = v3.sub(&v2);
let v1_denom = py.add(&py);
let v1_denom = v1_denom.mul(&basis.q.z);
let v1_denom = v1_denom.mul(&basis.pmq.z);
let qx = basis.q.x.mul(&v1_denom);
let qz = basis.q.z.mul(&v1_denom);
let qz_sq = qz.sqr();
let qy_jac = qy_num.mul(&qz_sq);
let qx_jac = qx.mul(&qz);
let jq = JacPoint::new(qx_jac, qy_jac, qz);
(jp, jq, ret)
}
#[inline]
pub fn lift_basis<L: FpBackend>(
basis: &mut EcBasis<L>,
curve: &mut EcCurve<L>,
) -> (JacPoint<L>, JacPoint<L>, Choice) {
let mut inverses = [basis.p.z.clone(), curve.c.clone()];
let mut t1 = [Fp2::<L>::zero(), Fp2::zero()];
let mut t2 = [Fp2::<L>::zero(), Fp2::zero()];
Fp2::batched_inv(&mut inverses, &mut t1, &mut t2);
basis.p.x = basis.p.x.mul(&inverses[0]);
basis.p.z = Fp2::one();
curve.a = curve.a.mul(&inverses[1]);
curve.c = Fp2::one();
lift_basis_normalized(basis, curve)
}
#[inline]
pub fn is_on_curve<L: FpBackend>(x: &Fp2<L>, curve: &EcCurve<L>) -> Choice {
let t0 = x.add(&curve.a); let t0 = t0.mul(x); let t0 = t0.add_one(); let t0 = t0.mul(x); t0.is_square()
}
#[inline]
fn clear_cofactor_for_maximal_even_order<L: FpBackend>(
p: &EcPoint<L>,
curve: &mut EcCurve<L>,
f: u32,
cofactor: &[u64],
cofactor_bitlen: usize,
torsion_even_power: u32,
) -> EcPoint<L> {
let mut result = super::point::ec_mul(p, cofactor, cofactor_bitlen, curve);
for _ in 0..(torsion_even_power - f) {
result = xdbl_a24(&result, &curve.a24, curve.is_a24_computed_and_normalized);
}
result
}
#[inline]
fn ec_basis_e0_2f<L: FpBackend>(
curve: &EcCurve<L>,
f: u32,
px_bytes: &[u8],
qx_bytes: &[u8],
torsion_even_power: u32,
) -> EcBasis<L> {
let px = Fp2::<L>::decode(px_bytes).expect("invariant: precomputed BASIS_E0_PX must decode");
let qx = Fp2::<L>::decode(qx_bytes).expect("invariant: precomputed BASIS_E0_QX must decode");
let mut p = EcPoint::new(px, Fp2::one());
let mut q = EcPoint::new(qx, Fp2::one());
for _ in 0..(torsion_even_power - f) {
p = xdbl_e0(&p);
q = xdbl_e0(&q);
}
let pmq = difference_point(&p, &q, curve);
EcBasis::new(p, q, pmq)
}
#[inline]
fn find_nqr_factor<L: FpBackend>(curve: &EcCurve<L>, start: u8) -> Result<(Fp2<L>, u16), ()> {
let mut n = start as u16;
loop {
let mut qr_b = true;
while qr_b {
if n >= 1024 {
return Err(());
}
let val = (n as u64) * (n as u64) + 1;
let tmp = Fp::<L>::from_small(val);
qr_b = bool::from(tmp.is_square());
n += 1;
}
let b = Fp::<L>::from_small((n - 1) as u64);
let z = Fp2 {
re: Fp::one(),
im: b.clone(),
};
let t0 = Fp2 {
re: Fp::zero(),
im: b,
};
let a_sq = curve.a.sqr();
let lhs = a_sq.mul(&t0);
let z_sq = z.sqr();
let disc = lhs.sub(&z_sq);
if !bool::from(disc.is_square()) {
let z_inv = z.inv();
let x = curve.a.mul(&z_inv).neg();
let hint = if n <= 128 { n - 1 } else { 0 };
return Ok((x, hint));
}
}
}
#[inline]
fn find_na_x_coord<L: FpBackend>(curve: &EcCurve<L>, start: u8) -> Result<(Fp2<L>, u16), ()> {
let mut n = start as u16;
let mut x = if n == 1 {
curve.a.clone()
} else {
curve.a.mul_small(n as u32)
};
while !bool::from(is_on_curve(&x, curve)) {
if n >= 1024 {
return Err(());
}
x = x.add(&curve.a);
n += 1;
}
let hint = if n < 128 { n } else { 0 };
Ok((x, hint))
}
#[inline]
pub fn ec_curve_to_basis_2f_to_hint<L: FpBackend>(
curve: &mut EcCurve<L>,
f: u32,
px_bytes: &[u8],
qx_bytes: &[u8],
cofactor: &[u64],
cofactor_bitlen: usize,
torsion_even_power: u32,
) -> Result<(EcBasis<L>, u8), ()> {
curve.normalize_curve_and_a24();
if bool::from(curve.a.ct_is_zero()) {
let basis = ec_basis_e0_2f(curve, f, px_bytes, qx_bytes, torsion_even_power);
return Ok((basis, 0));
}
let hint_a = bool::from(curve.a.is_square());
let (px, hint) = if !hint_a {
find_na_x_coord(curve, 1)?
} else {
find_nqr_factor(curve, 1)?
};
let p = EcPoint::new(px.clone(), Fp2::one());
let qx = curve.a.add(&px).neg();
let q = EcPoint::new(qx, Fp2::one());
let p = clear_cofactor_for_maximal_even_order(
&p,
curve,
f,
cofactor,
cofactor_bitlen,
torsion_even_power,
);
let q = clear_cofactor_for_maximal_even_order(
&q,
curve,
f,
cofactor,
cofactor_bitlen,
torsion_even_power,
);
let basis_q = difference_point(&p, &q, curve);
let hint_byte = ((hint as u8) << 1) | (hint_a as u8);
Ok((EcBasis::new(p, basis_q, q), hint_byte))
}
#[allow(clippy::too_many_arguments)]
#[inline]
pub fn ec_curve_to_basis_2f_from_hint<L: FpBackend>(
curve: &mut EcCurve<L>,
f: u32,
hint: u8,
px_bytes: &[u8],
qx_bytes: &[u8],
cofactor: &[u64],
cofactor_bitlen: usize,
torsion_even_power: u32,
) -> Result<(EcBasis<L>, i32), ()> {
curve.normalize_curve_and_a24();
if bool::from(curve.a.ct_is_zero()) {
let basis = ec_basis_e0_2f(curve, f, px_bytes, qx_bytes, torsion_even_power);
return Ok((basis, 1));
}
let hint_a = (hint & 1) != 0;
let hint_p = hint >> 1;
let px = if hint_p == 0 {
if !hint_a {
find_na_x_coord(curve, 128)?.0
} else {
find_nqr_factor(curve, 128)?.0
}
} else if !hint_a {
curve.a.mul_small(hint_p as u32)
} else {
let z = Fp2 {
re: Fp::one(),
im: Fp::<L>::from_small(hint_p as u64),
};
let z_inv = z.inv();
curve.a.mul(&z_inv).neg()
};
let p = EcPoint::new(px.clone(), Fp2::one());
let qx = curve.a.add(&px).neg();
let q = EcPoint::new(qx, Fp2::one());
let p = clear_cofactor_for_maximal_even_order(
&p,
curve,
f,
cofactor,
cofactor_bitlen,
torsion_even_power,
);
let q = clear_cofactor_for_maximal_even_order(
&q,
curve,
f,
cofactor,
cofactor_bitlen,
torsion_even_power,
);
let basis_q = difference_point(&p, &q, curve);
Ok((EcBasis::new(p, basis_q, q), 1))
}