Skip to content

Commit

Permalink
work with curves that have x=0, y=0 as point on the curve
Browse files Browse the repository at this point in the history
  • Loading branch information
tomato42 committed Aug 16, 2024
1 parent ac90159 commit 30067c2
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 19 deletions.
34 changes: 18 additions & 16 deletions src/ecdsa/ellipticcurve.py
Original file line number Diff line number Diff line change
Expand Up @@ -633,7 +633,7 @@ def __eq__(self, other):
"""
x1, y1, z1 = self.__coords
if other is INFINITY:
return (not x1 and not y1) or not z1
return not z1
if isinstance(other, Point):
x2, y2, z2 = other.x(), other.y(), 1
elif isinstance(other, PointJacobi):
Expand Down Expand Up @@ -760,7 +760,7 @@ def _double_with_z_1(self, X1, Y1, p, a):
# http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-mdbl-2007-bl
XX, YY = X1 * X1 % p, Y1 * Y1 % p
if not YY:
return 0, 0, 1
return 0, 0, 0
YYYY = YY * YY % p
S = 2 * ((X1 + YY) ** 2 - XX - YYYY) % p
M = 3 * XX + a
Expand All @@ -774,13 +774,13 @@ def _double(self, X1, Y1, Z1, p, a):
"""Add a point to itself, arbitrary z."""
if Z1 == 1:
return self._double_with_z_1(X1, Y1, p, a)
if not Y1 or not Z1:
return 0, 0, 1
if not Z1:
return 0, 0, 0
# after:
# http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-dbl-2007-bl
XX, YY = X1 * X1 % p, Y1 * Y1 % p
if not YY:
return 0, 0, 1
return 0, 0, 0
YYYY = YY * YY % p
ZZ = Z1 * Z1 % p
S = 2 * ((X1 + YY) ** 2 - XX - YYYY) % p
Expand All @@ -796,14 +796,14 @@ def double(self):
"""Add a point to itself."""
X1, Y1, Z1 = self.__coords

if not Y1:
if not Z1:
return INFINITY

p, a = self.__curve.p(), self.__curve.a()

X3, Y3, Z3 = self._double(X1, Y1, Z1, p, a)

if not Y3 and not X3:
if not Z3:
return INFINITY
return PointJacobi(self.__curve, X3, Y3, Z3, self.__order)

Expand Down Expand Up @@ -887,9 +887,9 @@ def __radd__(self, other):

def _add(self, X1, Y1, Z1, X2, Y2, Z2, p):
"""add two points, select fastest method."""
if (not X1 and not Y1) or not Z1:
if not Z1:
return X2 % p, Y2 % p, Z2 % p
if (not X2 and not Y2) or not Z2:
if not Z2:
return X1 % p, Y1 % p, Z1 % p
if Z1 == Z2:
if Z1 == 1:
Expand Down Expand Up @@ -918,7 +918,7 @@ def __add__(self, other):

X3, Y3, Z3 = self._add(X1, Y1, Z1, X2, Y2, Z2, p)

if (not X3 and not Y3) or not Z3:
if not Z3:
return INFINITY
return PointJacobi(self.__curve, X3, Y3, Z3, self.__order)

Expand All @@ -928,7 +928,7 @@ def __rmul__(self, other):

def _mul_precompute(self, other):
"""Multiply point by integer with precomputation table."""
X3, Y3, Z3, p = 0, 0, 1, self.__curve.p()
X3, Y3, Z3, p = 0, 0, 0, self.__curve.p()
_add = self._add
for X2, Y2 in self.__precompute:
if other % 2:
Expand All @@ -941,7 +941,7 @@ def _mul_precompute(self, other):
else:
other //= 2

if not Y3 or not Z3:
if not Z3:
return INFINITY
return PointJacobi(self.__curve, X3, Y3, Z3, self.__order)

Expand All @@ -960,7 +960,7 @@ def __mul__(self, other):

self = self.scale()
X2, Y2, _ = self.__coords
X3, Y3, Z3 = 0, 0, 1
X3, Y3, Z3 = 0, 0, 0
p, a = self.__curve.p(), self.__curve.a()
_double = self._double
_add = self._add
Expand All @@ -973,7 +973,7 @@ def __mul__(self, other):
elif i > 0:
X3, Y3, Z3 = _add(X3, Y3, Z3, X2, Y2, 1, p)

if (not X3 and not Y3) or not Z3:
if not Z3:
return INFINITY

return PointJacobi(self.__curve, X3, Y3, Z3, self.__order)
Expand Down Expand Up @@ -1002,7 +1002,7 @@ def mul_add(self, self_mul, other, other_mul):
other_mul = other_mul % self.__order

# (X3, Y3, Z3) is the accumulator
X3, Y3, Z3 = 0, 0, 1
X3, Y3, Z3 = 0, 0, 0
p, a = self.__curve.p(), self.__curve.a()

# as we have 6 unique points to work with, we can't scale all of them,
Expand Down Expand Up @@ -1071,7 +1071,7 @@ def mul_add(self, self_mul, other, other_mul):
assert B > 0
X3, Y3, Z3 = _add(X3, Y3, Z3, pApB_X, pApB_Y, pApB_Z, p)

if (not X3 and not Y3) or not Z3:
if not Z3:
return INFINITY

return PointJacobi(self.__curve, X3, Y3, Z3, self.__order)
Expand Down Expand Up @@ -1155,6 +1155,8 @@ def __eq__(self, other):
Note: only points that lay on the same curve can be equal.
"""
if other is INFINITY:
return self.__x is None or self.__y is None
if isinstance(other, Point):
return (
self.__curve == other.__curve
Expand Down
6 changes: 6 additions & 0 deletions src/ecdsa/test_ellipticcurve.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,12 @@ def test_inequality_points_diff_types(self):
c = CurveFp(100, -3, 100)
self.assertNotEqual(self.g_23, c)

def test_inequality_diff_y(self):
p1 = Point(self.c_23, 6, 4)
p2 = Point(self.c_23, 6, 19)

self.assertNotEqual(p1, p2)

def test_to_bytes_from_bytes(self):
p = Point(self.c_23, 3, 10)

Expand Down
121 changes: 118 additions & 3 deletions src/ecdsa/test_jacobi.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import hypothesis.strategies as st
from hypothesis import given, assume, settings, example

from .ellipticcurve import CurveFp, PointJacobi, INFINITY
from .ellipticcurve import CurveFp, PointJacobi, INFINITY, Point
from .ecdsa import (
generator_256,
curve_256,
Expand Down Expand Up @@ -144,7 +144,7 @@ def test_add_with_infinity(self):

def test_add_zero_point_to_affine(self):
pa = PointJacobi.from_affine(generator_256).to_affine()
pj = PointJacobi(curve_256, 0, 0, 1)
pj = PointJacobi(curve_256, 0, 0, 0)

s = pj + pa

Expand Down Expand Up @@ -195,8 +195,35 @@ def test_compare_non_zero_with_infinity(self):

self.assertNotEqual(pj, INFINITY)

def test_compare_non_zero_bad_scale_with_infinity(self):
pj = PointJacobi(curve_256, 1, 1, 0)
self.assertEqual(pj, INFINITY)

def test_eq_x_0_on_curve_with_infinity(self):
c_23 = CurveFp(23, 1, 1)
pj = PointJacobi(c_23, 0, 1, 1)

self.assertTrue(c_23.contains_point(0, 1))

self.assertNotEqual(pj, INFINITY)

def test_eq_y_0_on_curve_with_infinity(self):
c_23 = CurveFp(23, 1, 1)
pj = PointJacobi(c_23, 4, 0, 1)

self.assertTrue(c_23.contains_point(4, 0))

self.assertNotEqual(pj, INFINITY)

def test_eq_with_same_x_different_y(self):
c_23 = CurveFp(23, 1, 1)
p_a = PointJacobi(c_23, 0, 22, 1)
p_b = PointJacobi(c_23, 0, 1, 1)

self.assertNotEqual(p_a, p_b)

def test_compare_zero_point_with_infinity(self):
pj = PointJacobi(curve_256, 0, 0, 1)
pj = PointJacobi(curve_256, 0, 0, 0)

self.assertEqual(pj, INFINITY)

Expand Down Expand Up @@ -651,6 +678,13 @@ def test_double_to_infinity(self):
self.assertEqual(p3, INFINITY)
self.assertIs(p3, INFINITY)

def test_double_to_x_0(self):
c_23_2 = CurveFp(23, 1, 2)
p = PointJacobi(c_23_2, 9, 2, 1)
p2 = p.double()

self.assertEqual((p2.x(), p2.y()), (0, 18))

def test_mul_to_infinity(self):
c_23 = CurveFp(23, 1, 1)
p = PointJacobi(c_23, 11, 20, 1)
Expand All @@ -671,6 +705,41 @@ def test_add_to_infinity(self):
self.assertEqual(p3, INFINITY)
self.assertIs(p3, INFINITY)

def test_mul_to_x_0(self):
c_23 = CurveFp(23, 1, 1)
p = PointJacobi(c_23, 9, 7, 1)

p2 = p * 13
self.assertEqual((p2.x(), p2.y()), (0, 22))

def test_mul_to_y_0(self):
c_23 = CurveFp(23, 1, 1)
p = PointJacobi(c_23, 9, 7, 1)

p2 = p * 14
self.assertEqual((p2.x(), p2.y()), (4, 0))

def test_add_to_x_0(self):
c_23 = CurveFp(23, 1, 1)
p = PointJacobi(c_23, 9, 7, 1)

p2 = p * 12 + p
self.assertEqual((p2.x(), p2.y()), (0, 22))

def test_add_to_y_0(self):
c_23 = CurveFp(23, 1, 1)
p = PointJacobi(c_23, 9, 7, 1)

p2 = p * 13 + p
self.assertEqual((p2.x(), p2.y()), (4, 0))

def test_add_diff_z_to_infinity(self):
c_23 = CurveFp(23, 1, 1)
p = PointJacobi(c_23, 9, 7, 1)

c = p * 20 + p * 8
self.assertIs(c, INFINITY)

def test_pickle(self):
pj = PointJacobi(curve=CurveFp(23, 1, 1, 1), x=2, y=3, z=1, order=1)
self.assertEqual(pickle.loads(pickle.dumps(pj)), pj)
Expand Down Expand Up @@ -781,3 +850,49 @@ def interrupter(barrier_start, barrier_end, lock_exit):
gen._PointJacobi__precompute,
generator_112r2._PointJacobi__precompute,
)

class TestZeroCurve(unittest.TestCase):
def setUp(self):
self.curve = CurveFp(23, 1, 0)

def test_zero_point_on_curve(self):
self.assertTrue(self.curve.contains_point(0, 0))

def test_double_to_0_0_point(self):
p = PointJacobi(self.curve, 1, 18, 1)

d = p.double()

self.assertNotEqual(d, INFINITY)
self.assertEqual((0, 0), (d.x(), d.y()))

def test_double_to_0_0_point_with_non_one_z(self):
z = 2
p = PointJacobi(self.curve, 1 * z**2, 18 * z**3, z)

d = p.double()

self.assertNotEqual(d, INFINITY)
self.assertEqual((0, 0), (d.x(), d.y()))

def test_mul_to_0_0_point(self):
p = PointJacobi(self.curve, 11, 13, 1)

d = p * 12

self.assertNotEqual(d, INFINITY)
self.assertEqual((0, 0), (d.x(), d.y()))

def test_double_of_0_0_point(self):
p = PointJacobi(self.curve, 0, 0, 1)

d = p.double()

self.assertIs(d, INFINITY)

def test_compare_to_old_implementation(self):
p = PointJacobi(self.curve, 11, 13, 1)
p_c = Point(self.curve, 11, 13)

for i in range(24):
self.assertEqual(p * i, p_c * i)

0 comments on commit 30067c2

Please sign in to comment.