From 30067c241dc2c042503f2829ce204a5817ff7183 Mon Sep 17 00:00:00 2001 From: Hubert Kario Date: Fri, 9 Aug 2024 01:14:38 +0200 Subject: [PATCH] work with curves that have x=0, y=0 as point on the curve --- src/ecdsa/ellipticcurve.py | 34 ++++----- src/ecdsa/test_ellipticcurve.py | 6 ++ src/ecdsa/test_jacobi.py | 121 +++++++++++++++++++++++++++++++- 3 files changed, 142 insertions(+), 19 deletions(-) diff --git a/src/ecdsa/ellipticcurve.py b/src/ecdsa/ellipticcurve.py index 6dedff82..63e9220b 100644 --- a/src/ecdsa/ellipticcurve.py +++ b/src/ecdsa/ellipticcurve.py @@ -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): @@ -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 @@ -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 @@ -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) @@ -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: @@ -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) @@ -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: @@ -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) @@ -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 @@ -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) @@ -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, @@ -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) @@ -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 diff --git a/src/ecdsa/test_ellipticcurve.py b/src/ecdsa/test_ellipticcurve.py index db5ac7c4..31f9e0d8 100644 --- a/src/ecdsa/test_ellipticcurve.py +++ b/src/ecdsa/test_ellipticcurve.py @@ -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) diff --git a/src/ecdsa/test_jacobi.py b/src/ecdsa/test_jacobi.py index bb06cce6..445287b7 100644 --- a/src/ecdsa/test_jacobi.py +++ b/src/ecdsa/test_jacobi.py @@ -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, @@ -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 @@ -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) @@ -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) @@ -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) @@ -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)