From a37671a7179c0f31b92d1eea7980c5d2bf3558c1 Mon Sep 17 00:00:00 2001 From: Alicja Kario Date: Fri, 5 Jun 2026 12:14:02 +0200 Subject: [PATCH 1/3] make INFINITY from Jacobi easier to print --- src/ecdsa/ellipticcurve.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ecdsa/ellipticcurve.py b/src/ecdsa/ellipticcurve.py index a982c1e4..4bed7e9f 100644 --- a/src/ecdsa/ellipticcurve.py +++ b/src/ecdsa/ellipticcurve.py @@ -1289,6 +1289,9 @@ def curve(self): def order(self): return self.__order + def to_affine(self): + return self + class PointEdwards(AbstractPoint): """Point on Twisted Edwards curve. From 6e6dbf112d7ea1b278a25b483d5ad7f2d1525855 Mon Sep 17 00:00:00 2001 From: Alicja Kario Date: Fri, 5 Jun 2026 12:15:03 +0200 Subject: [PATCH 2/3] ensure that the points are really the same before doubling --- src/ecdsa/ellipticcurve.py | 9 +++++---- src/ecdsa/test_ellipticcurve.py | 10 ++++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/ecdsa/ellipticcurve.py b/src/ecdsa/ellipticcurve.py index 4bed7e9f..7ea8d99e 100644 --- a/src/ecdsa/ellipticcurve.py +++ b/src/ecdsa/ellipticcurve.py @@ -1185,14 +1185,15 @@ def __add__(self, other): if self == INFINITY: return other assert self.__curve == other.__curve - if self.__x == other.__x: - if (self.__y + other.__y) % self.__curve.p() == 0: + + p = self.__curve.p() + + if (self.__x - other.__x) % p == 0: + if (self.__y + other.__y) % p == 0: return INFINITY else: return self.double() - p = self.__curve.p() - l = ( (other.__y - self.__y) * numbertheory.inverse_mod(other.__x - self.__x, p) diff --git a/src/ecdsa/test_ellipticcurve.py b/src/ecdsa/test_ellipticcurve.py index 864cf108..415cbfa6 100644 --- a/src/ecdsa/test_ellipticcurve.py +++ b/src/ecdsa/test_ellipticcurve.py @@ -146,6 +146,16 @@ def setUpClass(cls): cls.c192 = CurveFp(p, -3, b) cls.p192 = Point(cls.c192, Gx, Gy, r) + def test_points_with_different_curves(self): + with self.assertRaises(AssertionError): + self.g_23 + self.p192 + + def test_add_point_to_negative(self): + self.assertIs(self.g_23 + (-self.g_23), INFINITY) + + def test_add_point_to_explicit_negative(self): + self.assertIs(self.g_23 + Point(self.c_23, 13, -7), INFINITY) + def test_p192(self): # Checking against some sample computations presented # in X9.62: From 8c5704e9e02c3d0adad960dc9db15744a59f2e1f Mon Sep 17 00:00:00 2001 From: Alicja Kario Date: Fri, 5 Jun 2026 12:30:26 +0200 Subject: [PATCH 3/3] use simple double and add algorithm in Point So, X9.62 looks to be trying to protect against side-channels from double and add by doing a "addition-subtraction method" in section D.3.2. For some reason, that doesn't work for all parameters (see issue #373). Given that we don't have a need for side-channel resistance, and this is not a faster algorithm than double and add, just use double and add. --- src/ecdsa/ellipticcurve.py | 35 ++++++++------------------------- src/ecdsa/test_ellipticcurve.py | 14 +++++++++++++ 2 files changed, 22 insertions(+), 27 deletions(-) diff --git a/src/ecdsa/ellipticcurve.py b/src/ecdsa/ellipticcurve.py index 7ea8d99e..f601f263 100644 --- a/src/ecdsa/ellipticcurve.py +++ b/src/ecdsa/ellipticcurve.py @@ -1207,13 +1207,6 @@ def __add__(self, other): def __mul__(self, other): """Multiply a point by an integer.""" - def leftmost_bit(x): - assert x > 0 - result = 1 - while result <= x: - result = 2 * result - return result // 2 - e = other if e == 0 or (self.__order and e % self.__order == 0): return INFINITY @@ -1222,26 +1215,14 @@ def leftmost_bit(x): if e < 0: return (-self) * (-e) - # From X9.62 D.3.2: - - e3 = 3 * e - negative_self = Point( - self.__curve, - self.__x, - (-self.__y) % self.__curve.p(), - self.__order, - ) - i = leftmost_bit(e3) // 2 - result = self - # print("Multiplying %s by %d (e3 = %d):" % (self, other, e3)) - while i > 1: - result = result.double() - if (e3 & i) != 0 and (e & i) == 0: - result = result + self - if (e3 & i) == 0 and (e & i) != 0: - result = result + negative_self - # print(". . . i = %d, result = %s" % ( i, result )) - i = i // 2 + i = e + temp = self + result = INFINITY + while i: + if i % 2 == 1: + result = result + temp + temp = temp.double() + i = i >> 1 return result diff --git a/src/ecdsa/test_ellipticcurve.py b/src/ecdsa/test_ellipticcurve.py index 415cbfa6..bcc0caa4 100644 --- a/src/ecdsa/test_ellipticcurve.py +++ b/src/ecdsa/test_ellipticcurve.py @@ -71,6 +71,20 @@ def test_add_and_mult_equivalence(p, m, check): assert p * m == check +# from https://github.com/tlsfuzzer/python-ecdsa/issues/373 +curve = CurveFp(p=31, a=2, b=3) +point = Point(curve, 6, 18) + + +@pytest.mark.parametrize( + "p, m, check", + [(point, n, exp) for n, exp in enumerate(add_n_times(point, 16))], + ids=["p31 test with mult {0}".format(i) for i in range(17)], +) +def test_add_and_mult_equivalence_2(p, m, check): + assert p * m == check + + class TestCurve(unittest.TestCase): @classmethod def setUpClass(cls):