Skip to content

Commit 30067c2

Browse files
committed
work with curves that have x=0, y=0 as point on the curve
1 parent ac90159 commit 30067c2

File tree

3 files changed

+142
-19
lines changed

3 files changed

+142
-19
lines changed

src/ecdsa/ellipticcurve.py

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,7 @@ def __eq__(self, other):
633633
"""
634634
x1, y1, z1 = self.__coords
635635
if other is INFINITY:
636-
return (not x1 and not y1) or not z1
636+
return not z1
637637
if isinstance(other, Point):
638638
x2, y2, z2 = other.x(), other.y(), 1
639639
elif isinstance(other, PointJacobi):
@@ -760,7 +760,7 @@ def _double_with_z_1(self, X1, Y1, p, a):
760760
# http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-mdbl-2007-bl
761761
XX, YY = X1 * X1 % p, Y1 * Y1 % p
762762
if not YY:
763-
return 0, 0, 1
763+
return 0, 0, 0
764764
YYYY = YY * YY % p
765765
S = 2 * ((X1 + YY) ** 2 - XX - YYYY) % p
766766
M = 3 * XX + a
@@ -774,13 +774,13 @@ def _double(self, X1, Y1, Z1, p, a):
774774
"""Add a point to itself, arbitrary z."""
775775
if Z1 == 1:
776776
return self._double_with_z_1(X1, Y1, p, a)
777-
if not Y1 or not Z1:
778-
return 0, 0, 1
777+
if not Z1:
778+
return 0, 0, 0
779779
# after:
780780
# http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-dbl-2007-bl
781781
XX, YY = X1 * X1 % p, Y1 * Y1 % p
782782
if not YY:
783-
return 0, 0, 1
783+
return 0, 0, 0
784784
YYYY = YY * YY % p
785785
ZZ = Z1 * Z1 % p
786786
S = 2 * ((X1 + YY) ** 2 - XX - YYYY) % p
@@ -796,14 +796,14 @@ def double(self):
796796
"""Add a point to itself."""
797797
X1, Y1, Z1 = self.__coords
798798

799-
if not Y1:
799+
if not Z1:
800800
return INFINITY
801801

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

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

806-
if not Y3 and not X3:
806+
if not Z3:
807807
return INFINITY
808808
return PointJacobi(self.__curve, X3, Y3, Z3, self.__order)
809809

@@ -887,9 +887,9 @@ def __radd__(self, other):
887887

888888
def _add(self, X1, Y1, Z1, X2, Y2, Z2, p):
889889
"""add two points, select fastest method."""
890-
if (not X1 and not Y1) or not Z1:
890+
if not Z1:
891891
return X2 % p, Y2 % p, Z2 % p
892-
if (not X2 and not Y2) or not Z2:
892+
if not Z2:
893893
return X1 % p, Y1 % p, Z1 % p
894894
if Z1 == Z2:
895895
if Z1 == 1:
@@ -918,7 +918,7 @@ def __add__(self, other):
918918

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

921-
if (not X3 and not Y3) or not Z3:
921+
if not Z3:
922922
return INFINITY
923923
return PointJacobi(self.__curve, X3, Y3, Z3, self.__order)
924924

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

929929
def _mul_precompute(self, other):
930930
"""Multiply point by integer with precomputation table."""
931-
X3, Y3, Z3, p = 0, 0, 1, self.__curve.p()
931+
X3, Y3, Z3, p = 0, 0, 0, self.__curve.p()
932932
_add = self._add
933933
for X2, Y2 in self.__precompute:
934934
if other % 2:
@@ -941,7 +941,7 @@ def _mul_precompute(self, other):
941941
else:
942942
other //= 2
943943

944-
if not Y3 or not Z3:
944+
if not Z3:
945945
return INFINITY
946946
return PointJacobi(self.__curve, X3, Y3, Z3, self.__order)
947947

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

961961
self = self.scale()
962962
X2, Y2, _ = self.__coords
963-
X3, Y3, Z3 = 0, 0, 1
963+
X3, Y3, Z3 = 0, 0, 0
964964
p, a = self.__curve.p(), self.__curve.a()
965965
_double = self._double
966966
_add = self._add
@@ -973,7 +973,7 @@ def __mul__(self, other):
973973
elif i > 0:
974974
X3, Y3, Z3 = _add(X3, Y3, Z3, X2, Y2, 1, p)
975975

976-
if (not X3 and not Y3) or not Z3:
976+
if not Z3:
977977
return INFINITY
978978

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

10041004
# (X3, Y3, Z3) is the accumulator
1005-
X3, Y3, Z3 = 0, 0, 1
1005+
X3, Y3, Z3 = 0, 0, 0
10061006
p, a = self.__curve.p(), self.__curve.a()
10071007

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

1074-
if (not X3 and not Y3) or not Z3:
1074+
if not Z3:
10751075
return INFINITY
10761076

10771077
return PointJacobi(self.__curve, X3, Y3, Z3, self.__order)
@@ -1155,6 +1155,8 @@ def __eq__(self, other):
11551155
11561156
Note: only points that lay on the same curve can be equal.
11571157
"""
1158+
if other is INFINITY:
1159+
return self.__x is None or self.__y is None
11581160
if isinstance(other, Point):
11591161
return (
11601162
self.__curve == other.__curve

src/ecdsa/test_ellipticcurve.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,12 @@ def test_inequality_points_diff_types(self):
251251
c = CurveFp(100, -3, 100)
252252
self.assertNotEqual(self.g_23, c)
253253

254+
def test_inequality_diff_y(self):
255+
p1 = Point(self.c_23, 6, 4)
256+
p2 = Point(self.c_23, 6, 19)
257+
258+
self.assertNotEqual(p1, p2)
259+
254260
def test_to_bytes_from_bytes(self):
255261
p = Point(self.c_23, 3, 10)
256262

src/ecdsa/test_jacobi.py

Lines changed: 118 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import hypothesis.strategies as st
1515
from hypothesis import given, assume, settings, example
1616

17-
from .ellipticcurve import CurveFp, PointJacobi, INFINITY
17+
from .ellipticcurve import CurveFp, PointJacobi, INFINITY, Point
1818
from .ecdsa import (
1919
generator_256,
2020
curve_256,
@@ -144,7 +144,7 @@ def test_add_with_infinity(self):
144144

145145
def test_add_zero_point_to_affine(self):
146146
pa = PointJacobi.from_affine(generator_256).to_affine()
147-
pj = PointJacobi(curve_256, 0, 0, 1)
147+
pj = PointJacobi(curve_256, 0, 0, 0)
148148

149149
s = pj + pa
150150

@@ -195,8 +195,35 @@ def test_compare_non_zero_with_infinity(self):
195195

196196
self.assertNotEqual(pj, INFINITY)
197197

198+
def test_compare_non_zero_bad_scale_with_infinity(self):
199+
pj = PointJacobi(curve_256, 1, 1, 0)
200+
self.assertEqual(pj, INFINITY)
201+
202+
def test_eq_x_0_on_curve_with_infinity(self):
203+
c_23 = CurveFp(23, 1, 1)
204+
pj = PointJacobi(c_23, 0, 1, 1)
205+
206+
self.assertTrue(c_23.contains_point(0, 1))
207+
208+
self.assertNotEqual(pj, INFINITY)
209+
210+
def test_eq_y_0_on_curve_with_infinity(self):
211+
c_23 = CurveFp(23, 1, 1)
212+
pj = PointJacobi(c_23, 4, 0, 1)
213+
214+
self.assertTrue(c_23.contains_point(4, 0))
215+
216+
self.assertNotEqual(pj, INFINITY)
217+
218+
def test_eq_with_same_x_different_y(self):
219+
c_23 = CurveFp(23, 1, 1)
220+
p_a = PointJacobi(c_23, 0, 22, 1)
221+
p_b = PointJacobi(c_23, 0, 1, 1)
222+
223+
self.assertNotEqual(p_a, p_b)
224+
198225
def test_compare_zero_point_with_infinity(self):
199-
pj = PointJacobi(curve_256, 0, 0, 1)
226+
pj = PointJacobi(curve_256, 0, 0, 0)
200227

201228
self.assertEqual(pj, INFINITY)
202229

@@ -651,6 +678,13 @@ def test_double_to_infinity(self):
651678
self.assertEqual(p3, INFINITY)
652679
self.assertIs(p3, INFINITY)
653680

681+
def test_double_to_x_0(self):
682+
c_23_2 = CurveFp(23, 1, 2)
683+
p = PointJacobi(c_23_2, 9, 2, 1)
684+
p2 = p.double()
685+
686+
self.assertEqual((p2.x(), p2.y()), (0, 18))
687+
654688
def test_mul_to_infinity(self):
655689
c_23 = CurveFp(23, 1, 1)
656690
p = PointJacobi(c_23, 11, 20, 1)
@@ -671,6 +705,41 @@ def test_add_to_infinity(self):
671705
self.assertEqual(p3, INFINITY)
672706
self.assertIs(p3, INFINITY)
673707

708+
def test_mul_to_x_0(self):
709+
c_23 = CurveFp(23, 1, 1)
710+
p = PointJacobi(c_23, 9, 7, 1)
711+
712+
p2 = p * 13
713+
self.assertEqual((p2.x(), p2.y()), (0, 22))
714+
715+
def test_mul_to_y_0(self):
716+
c_23 = CurveFp(23, 1, 1)
717+
p = PointJacobi(c_23, 9, 7, 1)
718+
719+
p2 = p * 14
720+
self.assertEqual((p2.x(), p2.y()), (4, 0))
721+
722+
def test_add_to_x_0(self):
723+
c_23 = CurveFp(23, 1, 1)
724+
p = PointJacobi(c_23, 9, 7, 1)
725+
726+
p2 = p * 12 + p
727+
self.assertEqual((p2.x(), p2.y()), (0, 22))
728+
729+
def test_add_to_y_0(self):
730+
c_23 = CurveFp(23, 1, 1)
731+
p = PointJacobi(c_23, 9, 7, 1)
732+
733+
p2 = p * 13 + p
734+
self.assertEqual((p2.x(), p2.y()), (4, 0))
735+
736+
def test_add_diff_z_to_infinity(self):
737+
c_23 = CurveFp(23, 1, 1)
738+
p = PointJacobi(c_23, 9, 7, 1)
739+
740+
c = p * 20 + p * 8
741+
self.assertIs(c, INFINITY)
742+
674743
def test_pickle(self):
675744
pj = PointJacobi(curve=CurveFp(23, 1, 1, 1), x=2, y=3, z=1, order=1)
676745
self.assertEqual(pickle.loads(pickle.dumps(pj)), pj)
@@ -781,3 +850,49 @@ def interrupter(barrier_start, barrier_end, lock_exit):
781850
gen._PointJacobi__precompute,
782851
generator_112r2._PointJacobi__precompute,
783852
)
853+
854+
class TestZeroCurve(unittest.TestCase):
855+
def setUp(self):
856+
self.curve = CurveFp(23, 1, 0)
857+
858+
def test_zero_point_on_curve(self):
859+
self.assertTrue(self.curve.contains_point(0, 0))
860+
861+
def test_double_to_0_0_point(self):
862+
p = PointJacobi(self.curve, 1, 18, 1)
863+
864+
d = p.double()
865+
866+
self.assertNotEqual(d, INFINITY)
867+
self.assertEqual((0, 0), (d.x(), d.y()))
868+
869+
def test_double_to_0_0_point_with_non_one_z(self):
870+
z = 2
871+
p = PointJacobi(self.curve, 1 * z**2, 18 * z**3, z)
872+
873+
d = p.double()
874+
875+
self.assertNotEqual(d, INFINITY)
876+
self.assertEqual((0, 0), (d.x(), d.y()))
877+
878+
def test_mul_to_0_0_point(self):
879+
p = PointJacobi(self.curve, 11, 13, 1)
880+
881+
d = p * 12
882+
883+
self.assertNotEqual(d, INFINITY)
884+
self.assertEqual((0, 0), (d.x(), d.y()))
885+
886+
def test_double_of_0_0_point(self):
887+
p = PointJacobi(self.curve, 0, 0, 1)
888+
889+
d = p.double()
890+
891+
self.assertIs(d, INFINITY)
892+
893+
def test_compare_to_old_implementation(self):
894+
p = PointJacobi(self.curve, 11, 13, 1)
895+
p_c = Point(self.curve, 11, 13)
896+
897+
for i in range(24):
898+
self.assertEqual(p * i, p_c * i)

0 commit comments

Comments
 (0)