Skip to content

Commit 8254038

Browse files
committed
handle non-prime order curves more gracefully
when the order of the curve is not a prime, then point doubling can return INFINITY, this will cause some negative values not to be reduced modulo curve p; fix this
1 parent eebe016 commit 8254038

File tree

3 files changed

+76
-11
lines changed

3 files changed

+76
-11
lines changed

src/ecdsa/ellipticcurve.py

+19-11
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 y1 or not z1
636+
return (not x1 and not y1) or not z1
637637
if isinstance(other, Point):
638638
x2, y2, z2 = other.x(), other.y(), 1
639639
elif isinstance(other, PointJacobi):
@@ -728,6 +728,7 @@ def to_affine(self):
728728
return INFINITY
729729
self.scale()
730730
x, y, z = self.__coords
731+
assert z == 1
731732
return Point(self.__curve, x, y, self.__order)
732733

733734
@staticmethod
@@ -802,7 +803,7 @@ def double(self):
802803

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

805-
if not Y3 or not Z3:
806+
if not Y3 and not X3:
806807
return INFINITY
807808
return PointJacobi(self.__curve, X3, Y3, Z3, self.__order)
808809

@@ -886,10 +887,10 @@ def __radd__(self, other):
886887

887888
def _add(self, X1, Y1, Z1, X2, Y2, Z2, p):
888889
"""add two points, select fastest method."""
889-
if not Y1 or not Z1:
890-
return X2, Y2, Z2
891-
if not Y2 or not Z2:
892-
return X1, Y1, Z1
890+
if (not X1 and not Y1) or not Z1:
891+
return X2 % p, Y2 % p, Z2 % p
892+
if (not X2 and not Y2) or not Z2:
893+
return X1 % p, Y1 % p, Z1 % p
893894
if Z1 == Z2:
894895
if Z1 == 1:
895896
return self._add_with_z_1(X1, Y1, X2, Y2, p)
@@ -917,7 +918,7 @@ def __add__(self, other):
917918

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

920-
if not Y3 or not Z3:
921+
if (not X3 and not Y3) or not Z3:
921922
return INFINITY
922923
return PointJacobi(self.__curve, X3, Y3, Z3, self.__order)
923924

@@ -972,7 +973,7 @@ def __mul__(self, other):
972973
elif i > 0:
973974
X3, Y3, Z3 = _add(X3, Y3, Z3, X2, Y2, 1, p)
974975

975-
if not Y3 or not Z3:
976+
if (not X3 and not Y3) or not Z3:
976977
return INFINITY
977978

978979
return PointJacobi(self.__curve, X3, Y3, Z3, self.__order)
@@ -1070,7 +1071,7 @@ def mul_add(self, self_mul, other, other_mul):
10701071
assert B > 0
10711072
X3, Y3, Z3 = _add(X3, Y3, Z3, pApB_X, pApB_Y, pApB_Z, p)
10721073

1073-
if not Y3 or not Z3:
1074+
if (not X3 and not Y3) or not Z3:
10741075
return INFINITY
10751076

10761077
return PointJacobi(self.__curve, X3, Y3, Z3, self.__order)
@@ -1220,7 +1221,12 @@ def leftmost_bit(x):
12201221
# From X9.62 D.3.2:
12211222

12221223
e3 = 3 * e
1223-
negative_self = Point(self.__curve, self.__x, -self.__y, self.__order)
1224+
negative_self = Point(
1225+
self.__curve,
1226+
self.__x,
1227+
(-self.__y) % self.__curve.p(),
1228+
self.__order,
1229+
)
12241230
i = leftmost_bit(e3) // 2
12251231
result = self
12261232
# print("Multiplying %s by %d (e3 = %d):" % (self, other, e3))
@@ -1247,7 +1253,6 @@ def __str__(self):
12471253

12481254
def double(self):
12491255
"""Return a new point that is twice the old."""
1250-
12511256
if self == INFINITY:
12521257
return INFINITY
12531258

@@ -1261,6 +1266,9 @@ def double(self):
12611266
* numbertheory.inverse_mod(2 * self.__y, p)
12621267
) % p
12631268

1269+
if not l:
1270+
return INFINITY
1271+
12641272
x3 = (l * l - 2 * self.__x) % p
12651273
y3 = (l * (self.__x - x3) - self.__y) % p
12661274

src/ecdsa/test_ellipticcurve.py

+27
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,33 @@ def test_double(self):
184184
self.assertEqual(p3.x(), x3)
185185
self.assertEqual(p3.y(), y3)
186186

187+
def test_double_to_infinity(self):
188+
p1 = Point(self.c_23, 11, 20)
189+
p2 = p1.double()
190+
self.assertEqual((p2.x(), p2.y()), (4, 0))
191+
self.assertNotEqual(p2, INFINITY)
192+
p3 = p2.double()
193+
self.assertEqual(p3, INFINITY)
194+
self.assertIs(p3, INFINITY)
195+
196+
def test_add_self_to_infinity(self):
197+
p1 = Point(self.c_23, 11, 20)
198+
p2 = p1 + p1
199+
self.assertEqual((p2.x(), p2.y()), (4, 0))
200+
self.assertNotEqual(p2, INFINITY)
201+
p3 = p2 + p2
202+
self.assertEqual(p3, INFINITY)
203+
self.assertIs(p3, INFINITY)
204+
205+
def test_mul_to_infinity(self):
206+
p1 = Point(self.c_23, 11, 20)
207+
p2 = p1 * 2
208+
self.assertEqual((p2.x(), p2.y()), (4, 0))
209+
self.assertNotEqual(p2, INFINITY)
210+
p3 = p2 * 2
211+
self.assertEqual(p3, INFINITY)
212+
self.assertIs(p3, INFINITY)
213+
187214
def test_multiply(self):
188215
x1, y1, m, x3, y3 = (3, 10, 2, 7, 12)
189216
p1 = Point(self.c_23, x1, y1)

src/ecdsa/test_jacobi.py

+30
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,36 @@ def test_add_with_point_at_infinity(self):
641641

642642
self.assertEqual((x, y, z), (2, 3, 1))
643643

644+
def test_double_to_infinity(self):
645+
c_23 = CurveFp(23, 1, 1)
646+
p = PointJacobi(c_23, 11, 20, 1)
647+
p2 = p.double()
648+
self.assertEqual((p2.x(), p2.y()), (4, 0))
649+
self.assertNotEqual(p2, INFINITY)
650+
p3 = p2.double()
651+
self.assertEqual(p3, INFINITY)
652+
self.assertIs(p3, INFINITY)
653+
654+
def test_mul_to_infinity(self):
655+
c_23 = CurveFp(23, 1, 1)
656+
p = PointJacobi(c_23, 11, 20, 1)
657+
p2 = p * 2
658+
self.assertEqual((p2.x(), p2.y()), (4, 0))
659+
self.assertNotEqual(p2, INFINITY)
660+
p3 = p2 * 2
661+
self.assertEqual(p3, INFINITY)
662+
self.assertIs(p3, INFINITY)
663+
664+
def test_add_to_infinity(self):
665+
c_23 = CurveFp(23, 1, 1)
666+
p = PointJacobi(c_23, 11, 20, 1)
667+
p2 = p + p
668+
self.assertEqual((p2.x(), p2.y()), (4, 0))
669+
self.assertNotEqual(p2, INFINITY)
670+
p3 = p2 + p2
671+
self.assertEqual(p3, INFINITY)
672+
self.assertIs(p3, INFINITY)
673+
644674
def test_pickle(self):
645675
pj = PointJacobi(curve=CurveFp(23, 1, 1, 1), x=2, y=3, z=1, order=1)
646676
self.assertEqual(pickle.loads(pickle.dumps(pj)), pj)

0 commit comments

Comments
 (0)