}
#[derive(Clone, PartialEq, Eq)]
+/// A Point, stored in Jacobian coordinates
pub(super) struct Point<C: Curve + ?Sized> {
x: C::CurveField,
y: C::CurveField,
}
impl<C: Curve + ?Sized> Point<C> {
+ fn check_curve_conditions() {
+ debug_assert!(C::ScalarModulus::PRIME < C::CurveModulus::PRIME, "N is < P");
+ }
+
fn on_curve(x: &C::CurveField, y: &C::CurveField) -> Result<(), ()> {
let x_2 = x.square();
let x_3 = x_2.mul(&x);
#[cfg(debug_assertions)]
fn on_curve_z(x: &C::CurveField, y: &C::CurveField, z: &C::CurveField) -> Result<(), ()> {
+ // m = 1 / z
+ // x_norm = x * m^2
+ // y_norm = y * m^3
+
let m = C::CurveField::from_modinv_of(z.clone().into_i())?;
let m_2 = m.square();
let m_3 = m_2.mul(&m);
}
fn from_xy(x: C::Int, y: C::Int) -> Result<Self, ()> {
+ Self::check_curve_conditions();
+
let x = C::CurveField::from_i(x);
let y = C::CurveField::from_i(y);
Self::on_curve(&x, &y)?;
/// Checks that `expected_x` is equal to our X affine coordinate (without modular inversion).
fn eq_x(&self, expected_x: &C::ScalarField) -> Result<(), ()> {
- debug_assert!(expected_x.clone().into_i() < C::CurveModulus::PRIME, "N is < P");
-
// If x is between N and P the below calculations will fail and we'll spuriously reject a
// signature and the wycheproof tests will fail. We should in theory accept such
// signatures, but the probability of this happening at random is roughly 1/2^128, i.e. we
// really don't need to handle it in practice. Thus, we only bother to do this in tests.
+ debug_assert!(expected_x.clone().into_i() < C::CurveModulus::PRIME, "N is < P");
+ debug_assert!(C::ScalarModulus::PRIME < C::CurveModulus::PRIME, "N is < P");
+ #[cfg(debug_assertions)] {
+ // Check the above assertion - ensure the difference between the modulus of the scalar
+ // and curve fields is less than half the bit length of our integers, which are at
+ // least 256 bit long.
+ let scalar_mod_on_curve = C::CurveField::from_i(C::ScalarModulus::PRIME);
+ let diff = C::CurveField::ZERO.sub(&scalar_mod_on_curve);
+ assert!(C::Int::BYTES * 8 / 2 >= 128, "We assume 256-bit ints and longer");
+ assert!(C::CurveModulus::PRIME.limbs()[0] > (1 << 63), "PRIME should have the top bit set");
+ assert!(C::ScalarModulus::PRIME.limbs()[0] > (1 << 63), "PRIME should have the top bit set");
+ let mut half_bitlen = C::CurveField::ONE;
+ for _ in 0..C::Int::BYTES * 8 / 2 {
+ half_bitlen = half_bitlen.double();
+ }
+ assert!(diff.into_i() < half_bitlen.into_i());
+ }
+
#[allow(unused_mut, unused_assignments)]
let mut slow_check = None;
#[cfg(test)] {
if self.y == C::CurveField::ZERO { return Err(()); }
if self.z == C::CurveField::ZERO { return Err(()); }
- let s = self.x.times_four().mul(&self.y.square());
- let z_2 = self.z.square();
- let z_4 = z_2.square();
- let y_2 = self.y.square();
- let y_4 = y_2.square();
- let x_2 = self.x.square();
- let m = x_2.times_three().add(&C::A.mul(&z_4));
- let x = m.square().sub(&s.double());
- let y = m.mul(&s.sub(&x)).sub(&y_4.times_eight());
- let z = self.y.double().mul(&self.z);
+ // https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b
+ // delta = Z1^2
+ // gamma = Y1^2
+ // beta = X1*gamma
+ // alpha = 3*(X1-delta)*(X1+delta)
+ // X3 = alpha^2-8*beta
+ // Z3 = (Y1+Z1)^2-gamma-delta
+ // Y3 = alpha*(4*beta-X3)-8*gamma^2
+
+ let delta = self.z.square();
+ let gamma = self.y.square();
+ let beta = self.x.mul(&gamma);
+ let alpha = self.x.sub(&delta).times_three().mul(&self.x.add(&delta));
+ let x = alpha.square().sub(&beta.times_eight());
+ let y = alpha.mul(&beta.times_four().sub(&x)).sub(&gamma.square().times_eight());
+ let z = self.y.add(&self.z).square().sub(&gamma).sub(&delta);
#[cfg(debug_assertions)] { assert!(Self::on_curve_z(&x, &y, &z).is_ok()); }
Ok(Point { x, y, z })
}
fn add(&self, o: &Self) -> Result<Self, ()> {
+ // https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-2007-bl
+ // Z1Z1 = Z1^2
+ // Z2Z2 = Z2^2
+ // U1 = X1*Z2Z2
+ // U2 = X2*Z1Z1
+ // S1 = Y1*Z2*Z2Z2
+ // S2 = Y2*Z1*Z1Z1
+ // H = U2-U1
+ // I = (2*H)^2
+ // J = H*I
+ // r = 2*(S2-S1)
+ // V = U1*I
+ // X3 = r^2-J-2*V
+ // Y3 = r*(V-X3)-2*S1*J
+ // Z3 = ((Z1+Z2)^2-Z1Z1-Z2Z2)*H
+
let o_z_2 = o.z.square();
let self_z_2 = self.z.square();
return self.double();
}
let h = u2.sub(&u1);
- let h_2 = h.square();
- let h_3 = h.mul(&h_2);
- let r = s2.sub(&s1);
- let x = r.square().sub(&h_3).sub(&u1.double().mul(&h_2));
- let y = r.mul(&u1.mul(&h_2).sub(&x)).sub(&s1.mul(&h_3));
- let z = h.mul(&self.z).mul(&o.z);
+ let i = h.double().square();
+ let j = h.mul(&i);
+ let r = s2.sub(&s1).double();
+ let v = u1.mul(&i);
+ let x = r.square().sub(&j).sub(&v.double());
+ let y = r.mul(&v.sub(&x)).sub(&s1.double().mul(&j));
+ let z = self.z.add(&o.z).square().sub(&self_z_2).sub(&o_z_2).mul(&h);
#[cfg(debug_assertions)] { assert!(Self::on_curve_z(&x, &y, &z).is_ok()); }
Ok(Point { x, y, z})