aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJ08nY2020-12-10 19:38:47 +0100
committerJ08nY2020-12-10 19:51:43 +0100
commitd2ef5abbb1c17c8630ebaa064f49c59a27ad0d4a (patch)
tree56e5bf6f0225eed92220ef6463d233ac8621d59e
parent3e78a690cf7f44293556dd5a5a3ef591390c6b93 (diff)
downloadpyecsca-d2ef5abbb1c17c8630ebaa064f49c59a27ad0d4a.tar.gz
pyecsca-d2ef5abbb1c17c8630ebaa064f49c59a27ad0d4a.tar.zst
pyecsca-d2ef5abbb1c17c8630ebaa064f49c59a27ad0d4a.zip
Add point compare using scaling formula.
-rw-r--r--pyecsca/ec/curve.py68
-rw-r--r--pyecsca/ec/mod.py14
-rw-r--r--pyecsca/ec/point.py34
-rw-r--r--test/ec/test_curve.py13
-rw-r--r--test/ec/test_point.py13
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")