use super::curve::{Arithmetic, Curve, CurveError, choose_root, sec1_decode, sec1_encode};
use super::edwards::Edwards;
use super::field::{Base, Field, reduced, sqrt_ratio, with_prime};
use super::group::{Group, bit_length};
use super::point::Point;
use super::weierstrass::{
Affine, Jacobian, Weierstrass, jacobian_equal, jacobian_group, normalize,
};
use crate::math::numbers::Number;
pub struct Montgomery {
base: Base,
a: Number,
b: Number,
a_limbs: Vec<u64>,
b_limbs: Vec<u64>,
a24_limbs: Vec<u64>,
a24_word: Option<u32>,
}
impl Montgomery {
pub fn new(a: &Number, b: &Number, p: &Number) -> Result<Montgomery, CurveError> {
let three = Number::new_from_u8(3, p.byte_order);
if p.is_negative() || *p <= three || !p.is_probable_prime() {
return Err(CurveError::NotPrime);
}
let a = reduced(a, p);
let b = reduced(b, p);
if b.is_zero() || ((&a * &a) - 4u8).rem_euclid(p).is_zero() {
return Err(CurveError::Singular);
}
let base = Base::new(p);
let n = with_prime!(&base.prime, f => f.limbs());
let (mut a_limbs, mut b_limbs, mut a24_limbs) = (vec![0; n], vec![0; n], vec![0; n]);
let four = Number::new_from_u8(4, p.byte_order);
let a24 = ((&a + 2u8) * four.mod_inverse(p).unwrap_or_default()).rem_euclid(p);
let a24_word = u32::try_from(&a24).ok();
with_prime!(&base.prime, f => {
f.store(&f.enter(&a), &mut a_limbs);
f.store(&f.enter(&b), &mut b_limbs);
f.store(&f.enter(&a24), &mut a24_limbs);
});
Ok(Montgomery {
base,
a,
b,
a_limbs,
b_limbs,
a24_limbs,
a24_word,
})
}
pub fn a(&self) -> &Number {
&self.a
}
pub fn b(&self) -> &Number {
&self.b
}
pub fn x_mul(&self, u: &Number, k: &Number) -> Number {
let scalar = super::field::limbs_of(k);
with_group!(self, g => {
let f = g.f;
let (x2, z2, _, _) = g.ladder(&f.enter(u), &scalar);
match f.invert(&z2) {
Some(i) => f.leave(&f.mul(&x2, &i)),
None => f.leave(&f.zero()),
}
})
}
pub fn to_weierstrass(&self) -> Weierstrass {
let (wa, wb) = self.weierstrass_coefficients();
Weierstrass::new(&wa, &wb, &self.base.p).unwrap_or_else(|_| unreachable!())
}
pub fn map_to_weierstrass<'t>(
&self,
target: &'t Weierstrass,
point: &Point<'_, Montgomery>,
) -> Point<'t, Weierstrass> {
point.check_curve(self);
let (wa, wb) = self.weierstrass_coefficients();
assert!(
target.modulus() == &self.base.p && (target.a(), target.b()) == (&wa, &wb),
"the target is not this curve's Weierstrass form"
);
let Some((x, y)) = point.coordinates() else {
return target.identity();
};
let (a, b, p) = (&self.a, &self.b, &self.base.p);
let inv_b = b.mod_inverse(p).unwrap_or_default();
let three = Number::new_from_u8(3, p.byte_order);
let shift = a * three.mod_inverse(p).unwrap_or_default();
let wx = ((x + shift) * &inv_b).rem_euclid(p);
let wy = (y * &inv_b).rem_euclid(p);
target.point(&wx, &wy).unwrap_or_else(|_| unreachable!())
}
pub fn to_edwards(&self) -> Result<Edwards, CurveError> {
let (ea, ed) = self.edwards_coefficients();
Edwards::new(&ea, &ed, &self.base.p)
}
pub fn map_to_edwards<'t>(
&self,
target: &'t Edwards,
point: &Point<'_, Montgomery>,
) -> Point<'t, Edwards> {
point.check_curve(self);
let p = &self.base.p;
let (ea, ed) = self.edwards_coefficients();
assert!(
target.modulus() == p && (target.a(), target.d()) == (&ea, &ed),
"the target is not this curve's Edwards form"
);
let Some((x, y)) = point.coordinates() else {
return target.identity();
};
let (ex, ey) = if x.is_zero() {
(x, (-Number::new_from_u8(1, p.byte_order)).rem_euclid(p))
} else {
let inv_y = y.mod_inverse(p).unwrap_or_default();
let inv = (&x + 1u8).mod_inverse(p).unwrap_or_default();
let ex = (&x * inv_y).rem_euclid(p);
let ey = ((x - 1u8) * inv).rem_euclid(p);
(ex, ey)
};
target.point(&ex, &ey).unwrap_or_else(|_| unreachable!())
}
fn weierstrass_coefficients(&self) -> (Number, Number) {
let (a, b, p) = (&self.a, &self.b, &self.base.p);
let i = |x: Number| x.rem_euclid(p).mod_inverse(p).unwrap_or_default();
let inv_b = i(b.clone());
let ib2 = (&inv_b * &inv_b).rem_euclid(p);
let ib3 = (&ib2 * &inv_b).rem_euclid(p);
let a2 = (a * a).rem_euclid(p);
let a3 = (&a2 * a).rem_euclid(p);
let wa = ((3u8 - &a2) * i(Number::new_from_u8(3, p.byte_order)) * ib2).rem_euclid(p);
let wb =
((a3 * 2u8 - a * 9u8) * i(Number::new_from_u8(27, p.byte_order)) * ib3).rem_euclid(p);
(wa, wb)
}
fn edwards_coefficients(&self) -> (Number, Number) {
let (a, b, p) = (&self.a, &self.b, &self.base.p);
let inv_b = b.mod_inverse(p).unwrap_or_default();
let ea = ((a + 2u8) * &inv_b).rem_euclid(p);
let ed = ((a - 2u8) * &inv_b).rem_euclid(p);
(ea, ed)
}
}
pub(super) struct Mon<'a, F: Field> {
f: &'a F,
a: F::E,
b: F::E,
a24: F::E,
a24_word: Option<u32>,
}
macro_rules! with_group {
($c:expr, $g:ident => $body:expr) => {
with_prime!(&$c.base.prime, f => {
let $g = Mon {
f,
a: f.load(&$c.a_limbs),
b: f.load(&$c.b_limbs),
a24: f.load(&$c.a24_limbs),
a24_word: $c.a24_word,
};
$body
})
};
}
use with_group;
impl<F: Field> Mon<'_, F> {
fn cubic(&self, x: &F::E) -> F::E {
let f = self.f;
f.mul(x, &f.add(&f.mul(x, &f.add(x, &self.a)), &f.one()))
}
fn ladder(&self, x1: &F::E, k: &[u64]) -> (F::E, F::E, F::E, F::E) {
let f = self.f;
let mut x = [f.one(), x1.clone()];
let mut z = [f.zero(), f.one()];
for t in (0..bit_length(k)).rev() {
let p = ((k[t / 64] >> (t % 64)) & 1) as usize;
let q = 1 - p;
let a = f.add(&x[p], &z[p]);
let aa = f.sqr(&a);
let b = f.sub(&x[p], &z[p]);
let bb = f.sqr(&b);
let e = f.sub(&aa, &bb);
let c = f.add(&x[q], &z[q]);
let d = f.sub(&x[q], &z[q]);
let da = f.mul(&d, &a);
let cb = f.mul(&c, &b);
x[q] = f.sqr(&f.add(&da, &cb));
z[q] = f.mul(x1, &f.sqr(&f.sub(&da, &cb)));
x[p] = f.mul(&aa, &bb);
let scaled = match self.a24_word {
Some(w) => f.mul_word(&e, u64::from(w), &self.a24),
None => f.mul(&self.a24, &e),
};
z[p] = f.mul(&e, &f.add(&bb, &scaled));
}
let [x2, x3] = x;
let [z2, z3] = z;
(x2, z2, x3, z3)
}
}
impl<F: Field> Group for Mon<'_, F> {
jacobian_group!();
fn double(&self, p: &Jacobian<F::E>) -> Jacobian<F::E> {
let f = self.f;
let xx = f.sqr(&p.x);
let zz = f.sqr(&p.z);
let yy = f.sqr(&p.y);
let l = f.add(
&f.add(&f.twice(&xx), &xx),
&f.add(&f.mul(&f.twice(&self.a), &f.mul(&p.x, &zz)), &f.sqr(&zz)),
);
let z3 = f.mul(&f.twice(&f.mul(&p.y, &p.z)), &self.b);
let byy = f.mul(&self.b, &yy);
let t = f.twice(&f.twice(&f.mul(&self.b, &f.mul(&p.x, &byy))));
let x3 = f.sub(
&f.sub(&f.mul(&self.b, &f.sqr(&l)), &f.mul(&self.a, &f.sqr(&z3))),
&f.twice(&t),
);
let b8 = f.twice(&f.twice(&f.twice(&f.mul(&self.b, &f.sqr(&byy)))));
let y3 = f.sub(&f.mul(&l, &f.sub(&t, &x3)), &b8);
Jacobian {
x: x3,
y: y3,
z: z3,
}
}
fn add(&self, p: &Jacobian<F::E>, q: &Jacobian<F::E>) -> Jacobian<F::E> {
let f = self.f;
if f.is_zero(&p.z) {
return q.clone();
}
if f.is_zero(&q.z) {
return p.clone();
}
let z1z1 = f.sqr(&p.z);
let z2z2 = f.sqr(&q.z);
let u1 = f.mul(&p.x, &z2z2);
let u2 = f.mul(&q.x, &z1z1);
let s1 = f.mul(&f.mul(&p.y, &q.z), &z2z2);
let s2 = f.mul(&f.mul(&q.y, &p.z), &z1z1);
let z12 = f.mul(&p.z, &q.z);
self.chord(&u1, &u2, &s1, &s2, &z12, p)
}
fn add_cached(&self, p: &Jacobian<F::E>, c: &Affine<F::E>) -> Jacobian<F::E> {
let f = self.f;
if c.infinity {
return p.clone();
}
if f.is_zero(&p.z) {
return self.of_affine(&c.x, &c.y);
}
let z1z1 = f.sqr(&p.z);
let u2 = f.mul(&c.x, &z1z1);
let s2 = f.mul(&f.mul(&c.y, &p.z), &z1z1);
self.chord(&p.x, &u2, &p.y, &s2, &p.z, p)
}
fn mul(&self, p: &Jacobian<F::E>, k: &[u64], negative: bool) -> Jacobian<F::E> {
let f = self.f;
let bits = bit_length(k);
if bits == 0 || f.is_zero(&p.z) {
return self.identity();
}
let affine = normalize(f, core::slice::from_ref(p));
let (x, y) = (&affine[0].x, &affine[0].y);
if f.is_zero(y) {
return if k[0] & 1 == 0 {
self.identity()
} else {
p.clone()
};
}
let (x2, z2, x3, z3) = self.ladder(x, k);
let r = if f.is_zero(&z2) {
self.identity()
} else if f.is_zero(&z3) {
self.neg(p)
} else {
let v1 = f.mul(x, &z2);
let v2 = f.add(&x2, &v1);
let v3 = f.mul(&f.sqr(&f.sub(&x2, &v1)), &x3);
let v1 = f.mul(&f.twice(&self.a), &z2);
let v2 = f.add(&v2, &v1);
let v4 = f.add(&f.mul(x, &x2), &z2);
let v2 = f.mul(&v2, &v4);
let v1 = f.mul(&v1, &z2);
let v2 = f.sub(&v2, &v1);
let v2 = f.mul(&v2, &z3);
let yy = f.sub(&v2, &v3);
let v1 = f.mul(&f.mul(&f.mul(&f.twice(&self.b), y), &z2), &z3);
let (xr, zr) = (f.mul(&v1, &x2), f.mul(&v1, &z2));
Jacobian {
x: f.mul(&xr, &zr),
y: f.mul(&yy, &f.sqr(&zr)),
z: zr,
}
};
if negative { self.neg(&r) } else { r }
}
}
impl<F: Field> Mon<'_, F> {
fn chord(
&self,
u1: &F::E,
u2: &F::E,
s1: &F::E,
s2: &F::E,
z12: &F::E,
p: &Jacobian<F::E>,
) -> Jacobian<F::E> {
let f = self.f;
let h = f.sub(u2, u1);
let r = f.sub(s2, s1);
if f.is_zero(&h) {
return if f.is_zero(&r) {
self.double(p)
} else {
self.identity()
};
}
let hh = f.sqr(&h);
let z3 = f.mul(z12, &h);
let x3 = f.sub(
&f.sub(&f.mul(&self.b, &f.sqr(&r)), &f.mul(&self.a, &f.sqr(&z3))),
&f.mul(&hh, &f.add(u1, u2)),
);
let y3 = f.sub(
&f.mul(&r, &f.sub(&f.mul(u1, &hh), &x3)),
&f.mul(s1, &f.mul(&h, &hh)),
);
Jacobian {
x: x3,
y: y3,
z: z3,
}
}
}
impl Arithmetic for Montgomery {
const COORDINATES: usize = 3;
fn base(&self) -> &Base {
&self.base
}
fn same_curve(&self, other: &Self) -> bool {
self.base.p == other.base.p && self.a == other.a && self.b == other.b
}
fn satisfied(&self, x: &Number, y: &Number) -> bool {
with_group!(self, g => {
let f = g.f;
f.equal(&f.mul(&g.b, &f.sqr(&f.enter(y))), &g.cubic(&f.enter(x)))
})
}
fn lift_limbs(&self, x: &Number, odd: bool) -> Option<Vec<u64>> {
with_group!(self, g => {
let f = g.f;
let ex = f.enter(x);
let y = sqrt_ratio(f, self.base.sqrt_plan(), &g.cubic(&ex), &g.b)?;
let y = choose_root(f, y, odd)?;
let mut r = vec![0; 3 * f.limbs()];
g.store(&g.of_affine(&ex, &y), &mut r);
Some(r)
})
}
fn encode(&self, xy: Option<(&Number, &Number)>, compressed: bool) -> Vec<u8> {
sec1_encode(self.base.bytes(), xy, compressed)
}
fn decode(&self, bytes: &[u8]) -> Option<Vec<u64>> {
sec1_decode(self, bytes)
}
super::curve::arithmetic!(with_group);
}
impl Curve for Montgomery {
fn discriminant(&self) -> Number {
let (a, b, p) = (&self.a, &self.b, &self.base.p);
let b2 = (b * b).rem_euclid(p);
let b6 = (&b2 * &b2 * &b2).rem_euclid(p);
(((a * a) - 4u8) * 16u8 * b6.mod_inverse(p).unwrap_or_default()).rem_euclid(p)
}
fn j_invariant(&self) -> Number {
let (a, p) = (&self.a, &self.base.p);
let a2 = (a * a).rem_euclid(p);
let t = (&a2 + (p - 3u8)).rem_euclid(p);
let d = ((&a2 + p) - 4u8).rem_euclid(p);
(&t * &t * &t * 256u16 * d.mod_inverse(p).unwrap_or_default()).rem_euclid(p)
}
}