diff options
| author | J08nY | 2020-12-10 19:38:47 +0100 |
|---|---|---|
| committer | J08nY | 2020-12-10 19:51:43 +0100 |
| commit | d2ef5abbb1c17c8630ebaa064f49c59a27ad0d4a (patch) | |
| tree | 56e5bf6f0225eed92220ef6463d233ac8621d59e | |
| parent | 3e78a690cf7f44293556dd5a5a3ef591390c6b93 (diff) | |
| download | pyecsca-d2ef5abbb1c17c8630ebaa064f49c59a27ad0d4a.tar.gz pyecsca-d2ef5abbb1c17c8630ebaa064f49c59a27ad0d4a.tar.zst pyecsca-d2ef5abbb1c17c8630ebaa064f49c59a27ad0d4a.zip | |
Add point compare using scaling formula.
| -rw-r--r-- | pyecsca/ec/curve.py | 68 | ||||
| -rw-r--r-- | pyecsca/ec/mod.py | 14 | ||||
| -rw-r--r-- | pyecsca/ec/point.py | 34 | ||||
| -rw-r--r-- | test/ec/test_curve.py | 13 | ||||
| -rw-r--r-- | test/ec/test_point.py | 13 |
5 files changed, 103 insertions, 39 deletions
diff --git a/pyecsca/ec/curve.py b/pyecsca/ec/curve.py index 722c980..9321826 100644 --- a/pyecsca/ec/curve.py +++ b/pyecsca/ec/curve.py @@ -32,7 +32,7 @@ class EllipticCurve(object): for name, value in parameters.items(): if isinstance(value, Mod): if value.n != prime: - raise ValueError + raise ValueError(f"Parameter {name} has wrong modulus.") else: value = Mod(value, prime) self.parameters[name] = value @@ -40,22 +40,26 @@ class EllipticCurve(object): def _execute_base_formulas(self, formulas: List[Module], *points: Point) -> Point: for point in points: - if point.coordinate_model.curve_model != self.model: - raise ValueError if not isinstance(point.coordinate_model, AffineCoordinateModel): - raise ValueError - locals = {var + str(i + 1): point.coords[var] - for i, point in enumerate(points) for var in point.coords} - locals.update(self.parameters) + raise ValueError("Coordinate model of point is not affine.") + if point.coordinate_model.curve_model != self.model: + raise ValueError("Curve model of point does not match the curve.") + locls = {var + str(i + 1): point.coords[var] + for i, point in enumerate(points) for var in point.coords} + locls.update(self.parameters) for line in formulas: - exec(compile(line, "", mode="exec"), None, locals) - if not isinstance(locals["x"], Mod): - locals["x"] = Mod(locals["x"], self.prime) - if not isinstance(locals["y"], Mod): - locals["y"] = Mod(locals["y"], self.prime) - return Point(AffineCoordinateModel(self.model), x=locals["x"], y=locals["y"]) + exec(compile(line, "", mode="exec"), None, locls) + if not isinstance(locls["x"], Mod): + locls["x"] = Mod(locls["x"], self.prime) + if not isinstance(locls["y"], Mod): + locls["y"] = Mod(locls["y"], self.prime) + return Point(AffineCoordinateModel(self.model), x=locls["x"], y=locls["y"]) def affine_add(self, one: Point, other: Point) -> Point: + """ + Add two affine points using the affine addition formula. + Handles the case of point at infinity gracefully. + """ if isinstance(one, InfinityPoint): return other if isinstance(other, InfinityPoint): @@ -65,22 +69,34 @@ class EllipticCurve(object): return self._execute_base_formulas(self.model.base_addition, one, other) def affine_double(self, one: Point) -> Point: + """ + Double an affine point using the affine doubling formula. + Handles the case of point at infinity gracefully. + """ if isinstance(one, InfinityPoint): return one return self._execute_base_formulas(self.model.base_doubling, one) def affine_negate(self, one: Point) -> Point: + """ + Negate an affine point using the affine negation formula. + Handles the case of point at infinity gracefully. + """ if isinstance(one, InfinityPoint): return one return self._execute_base_formulas(self.model.base_negation, one) def affine_multiply(self, point: Point, scalar: int) -> Point: - if point.coordinate_model.curve_model != self.model: - raise ValueError - if not isinstance(point.coordinate_model, AffineCoordinateModel): - raise ValueError + """ + Multiply an affine point by a scalar using the affine doubling and addition formulas. + Handles the case of point at infinity gracefully. + """ if isinstance(point, InfinityPoint): return point + if not isinstance(point.coordinate_model, AffineCoordinateModel): + raise ValueError("Coordinate model of point is not affine.") + if point.coordinate_model.curve_model != self.model: + raise ValueError("Curve model of point does not match the curve.") q = copy(point) r = copy(point) @@ -92,16 +108,20 @@ class EllipticCurve(object): @property def affine_neutral(self) -> Optional[Point]: + """ + Get the neutral point in affine form, if it has one, otherwise `None`. + :return: The affine neutral point or `None`. + """ if not self.neutral_is_affine: return None - locals = {**self.parameters} + locls = {**self.parameters} for line in self.model.base_neutral: - exec(compile(line, "", mode="exec"), None, locals) - if not isinstance(locals["x"], Mod): - locals["x"] = Mod(locals["x"], self.prime) - if not isinstance(locals["y"], Mod): - locals["y"] = Mod(locals["y"], self.prime) - return Point(AffineCoordinateModel(self.model), x=locals["x"], y=locals["y"]) + exec(compile(line, "", mode="exec"), None, locls) + if not isinstance(locls["x"], Mod): + locls["x"] = Mod(locls["x"], self.prime) + if not isinstance(locls["y"], Mod): + locls["y"] = Mod(locls["y"], self.prime) + return Point(AffineCoordinateModel(self.model), x=locls["x"], y=locls["y"]) @property def neutral_is_affine(self): diff --git a/pyecsca/ec/mod.py b/pyecsca/ec/mod.py index 2e6e07a..a28949c 100644 --- a/pyecsca/ec/mod.py +++ b/pyecsca/ec/mod.py @@ -224,11 +224,10 @@ class RawMod(BaseMod): """ if not miller_rabin(self.n): raise NotImplementedError + if self.x == 0: + return RawMod(0, self.n) if not self.is_residue(): - if self.x == 0: - return RawMod(0, self.n) - else: - raise NonResidueError("No square root exists.") + raise NonResidueError("No square root exists.") if self.n % 4 == 3: return self ** int((self.n + 1) // 4) q = self.n - 1 @@ -403,11 +402,10 @@ if has_gmp: """ if not gmpy2.is_prime(self.n): raise NotImplementedError + if self.x == 0: + return GMPMod(0, self.n) if not self.is_residue(): - if self.x == 0: - return GMPMod(0, self.n) - else: - raise NonResidueError("No square root exists.") + raise NonResidueError("No square root exists.") if self.n % 4 == 3: return self ** int((self.n + 1) // 4) q = self.n - 1 diff --git a/pyecsca/ec/point.py b/pyecsca/ec/point.py index a0a1879..19c2483 100644 --- a/pyecsca/ec/point.py +++ b/pyecsca/ec/point.py @@ -111,9 +111,24 @@ class Point(object): raise NotImplementedError return action.exit(Point(coordinate_model, **result)) - def equals(self, other: Any) -> bool: + def equals_affine(self, other: "Point") -> bool: """Test whether this point is equal to `other` irrespective of the coordinate model (in the affine sense).""" - if not isinstance(other, Point): + if not isinstance(other, Point) or isinstance(other, InfinityPoint): + return False + if self.coordinate_model.curve_model != other.coordinate_model.curve_model: + return False + return self.to_affine() == other.to_affine() + + def equals_scaled(self, other: "Point") -> bool: + """ + Test whether this point is equal to `other` using the "z" scaling formula, + which maps the projective class to a single representative. + + :param other: The point to compare + :raises ValueError: If the "z" formula is not available for the coordinate system. + :return: Whether the points are equal. + """ + if not isinstance(other, Point) or isinstance(other, InfinityPoint): return False if self.coordinate_model.curve_model != other.coordinate_model.curve_model: return False @@ -122,7 +137,12 @@ class Point(object): self_mapped = formula(self) other_mapped = formula(other) return self_mapped == other_mapped - return self.to_affine() == other.to_affine() + else: + raise ValueError("No scaling formula available.") + + def equals(self, other: "Point") -> bool: + """Test whether this point is equal to `other` irrespective of the coordinate model (in the affine sense).""" + return self.equals_affine(other) def __bytes__(self): res = b"\x04" @@ -162,7 +182,13 @@ class InfinityPoint(Point): def to_model(self, coordinate_model: CoordinateModel, curve: "EllipticCurve") -> "InfinityPoint": return InfinityPoint(coordinate_model) - def equals(self, other) -> bool: + def equals_affine(self, other: "Point") -> bool: + return self == other + + def equals_scaled(self, other: "Point") -> bool: + return self == other + + def equals(self, other: "Point") -> bool: return self == other def __bytes__(self): diff --git a/test/ec/test_curve.py b/test/ec/test_curve.py index d15228a..cbc1d14 100644 --- a/test/ec/test_curve.py +++ b/test/ec/test_curve.py @@ -58,12 +58,20 @@ class CurveTests(TestCase): added = self.secp128r1.curve.affine_add(self.affine_base, self.affine_base) doubled = self.secp128r1.curve.affine_double(self.affine_base) self.assertEqual(added, doubled) + self.assertEqual(self.secp128r1.curve.affine_add(self.secp128r1.curve.neutral, pt), pt) + self.assertEqual(self.secp128r1.curve.affine_add(pt, self.secp128r1.curve.neutral), pt) def test_affine_double(self): self.assertIsNotNone(self.secp128r1.curve.affine_double(self.affine_base)) + self.assertEqual(self.secp128r1.curve.affine_double(self.secp128r1.curve.neutral), self.secp128r1.curve.neutral) def test_affine_negate(self): self.assertIsNotNone(self.secp128r1.curve.affine_negate(self.affine_base)) + self.assertEqual(self.secp128r1.curve.affine_negate(self.secp128r1.curve.neutral), self.secp128r1.curve.neutral) + with self.assertRaises(ValueError): + self.secp128r1.curve.affine_negate(self.base) + with self.assertRaises(ValueError): + self.secp128r1.curve.affine_negate(self.curve25519.generator) def test_affine_multiply(self): expected = self.affine_base @@ -72,6 +80,11 @@ class CurveTests(TestCase): expected = self.secp128r1.curve.affine_add(expected, self.affine_base) expected = self.secp128r1.curve.affine_double(expected) self.assertEqual(self.secp128r1.curve.affine_multiply(self.affine_base, 10), expected) + self.assertEqual(self.secp128r1.curve.affine_multiply(self.secp128r1.curve.neutral, 10), self.secp128r1.curve.neutral) + with self.assertRaises(ValueError): + self.secp128r1.curve.affine_multiply(self.base, 10) + with self.assertRaises(ValueError): + self.secp128r1.curve.affine_multiply(self.curve25519.generator, 10) def test_affine_neutral(self): self.assertIsNone(self.secp128r1.curve.affine_neutral) diff --git a/test/ec/test_point.py b/test/ec/test_point.py index b8d355a..0fabbf1 100644 --- a/test/ec/test_point.py +++ b/test/ec/test_point.py @@ -35,8 +35,7 @@ class PointTests(TestCase): self.assertIsInstance(affine, InfinityPoint) def test_to_model(self): - affine = Point(self.affine, x=Mod(0xabcd, self.secp128r1.curve.prime), - y=Mod(0xef, self.secp128r1.curve.prime)) + affine = Point(self.affine, x=Mod(0xabcd, self.secp128r1.curve.prime), y=Mod(0xef, self.secp128r1.curve.prime)) projective_model = self.coords other = affine.to_model(projective_model, self.secp128r1.curve) @@ -80,11 +79,19 @@ class PointTests(TestCase): self.assertNotEqual(pt, 2) self.assertFalse(pt.equals(third)) self.assertNotEqual(pt, third) + self.assertTrue(pt.equals_scaled(other)) + self.assertTrue(pt.equals_affine(other)) + self.assertFalse(pt.equals_scaled(third)) infty_one = InfinityPoint(self.coords) infty_other = InfinityPoint(self.coords) self.assertTrue(infty_one.equals(infty_other)) + self.assertTrue(infty_one.equals_affine(infty_other)) + self.assertTrue(infty_one.equals_scaled(infty_other)) self.assertEqual(infty_one, infty_other) + self.assertFalse(pt.equals(infty_one)) + self.assertFalse(pt.equals_affine(infty_one)) + self.assertFalse(pt.equals_scaled(infty_one)) mont = MontgomeryModel() different = Point(mont.coordinates["xz"], @@ -100,4 +107,4 @@ class PointTests(TestCase): Y=Mod(0x6, self.secp128r1.curve.prime), Z=Mod(2, self.secp128r1.curve.prime)) self.assertEqual(bytes(pt), b"\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02") - self.assertEqual(bytes(InfinityPoint(self.coords)), b"\x00")
\ No newline at end of file + self.assertEqual(bytes(InfinityPoint(self.coords)), b"\x00") |
