aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJ08nY2021-01-06 15:47:58 +0100
committerJ08nY2021-01-06 15:47:58 +0100
commit9dd00af7aede2085812a65a888a7539e9a792213 (patch)
tree03ebbc2fbcf6b886e274049939e6c3a98e65e430
parent9a3bc0379b5033e03dec6473fa645c4bf1770105 (diff)
downloadpyecsca-9dd00af7aede2085812a65a888a7539e9a792213.tar.gz
pyecsca-9dd00af7aede2085812a65a888a7539e9a792213.tar.zst
pyecsca-9dd00af7aede2085812a65a888a7539e9a792213.zip
Resolve out rationals introduced by SymPy.
Fixes #9.
-rw-r--r--Makefile2
-rw-r--r--pyecsca/ec/formula.py46
-rw-r--r--test/ec/test_regress.py15
3 files changed, 50 insertions, 13 deletions
diff --git a/Makefile b/Makefile
index 9ebe608..2eaf069 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
EC_TESTS = ec.test_context ec.test_configuration ec.test_curve ec.test_formula \
ec.test_params ec.test_key_agreement ec.test_key_generation ec.test_mod ec.test_model \
-ec.test_mult ec.test_naf ec.test_op ec.test_point ec.test_signature ec.test_transformations
+ec.test_mult ec.test_naf ec.test_op ec.test_point ec.test_signature ec.test_transformations ec.test_regress
SCA_TESTS = sca.test_align sca.test_combine sca.test_edit sca.test_filter sca.test_match sca.test_process \
sca.test_sampling sca.test_target sca.test_test sca.test_trace sca.test_traceset sca.test_plot
diff --git a/pyecsca/ec/formula.py b/pyecsca/ec/formula.py
index fea5a9d..38701cb 100644
--- a/pyecsca/ec/formula.py
+++ b/pyecsca/ec/formula.py
@@ -6,7 +6,7 @@ from typing import List, Set, Any, ClassVar, MutableMapping, Tuple, Union, Dict
from pkg_resources import resource_stream
from public import public
-from sympy import sympify, FF, symbols, Poly
+from sympy import sympify, FF, symbols, Poly, Rational
from .context import ResultAction, getcontext, NullContext
from .error import UnsatisfiedAssumptionError, raise_unsatisified_assumption
@@ -113,15 +113,7 @@ class Formula(ABC):
unified: bool
"""Whether the formula is specifies that it is unified."""
- def __call__(self, *points: Any, **params: Mod) -> Tuple[Any, ...]:
- """
- Execute a formula.
-
- :param points: Points to pass into the formula.
- :param params: Parameters of the curve.
- :return: The resulting point(s).
- """
- from .point import Point
+ def __validate_points(self, points, params):
# Validate number of inputs.
if len(points) != self.num_inputs:
raise ValueError(f"Wrong number of inputs for {self}.")
@@ -131,8 +123,9 @@ class Formula(ABC):
raise ValueError(f"Wrong coordinate model of point {point}.")
for coord, value in point.coords.items():
params[coord + str(i + 1)] = value
+
+ def __validate_assumptions(self, field, params):
# Validate assumptions and compute formula parameters.
- field = int(params[next(iter(params.keys()))].n) # TODO: This is nasty...
for assumption in self.assumptions:
assumption_string = unparse(assumption)[1:-2]
lhs, rhs = assumption_string.split(" == ")
@@ -147,12 +140,28 @@ class Formula(ABC):
f"Unsatisfied assumption in the formula ({assumption_string}).")
else:
k = FF(field)
- expr = sympify(f"{rhs} - {lhs}")
+ expr = sympify(f"{rhs} - {lhs}", evaluate=False)
for curve_param, value in params.items():
expr = expr.subs(curve_param, k(value))
if len(expr.free_symbols) > 1 or (param := str(expr.free_symbols.pop())) not in self.parameters:
raise ValueError(
f"This formula couldn't be executed due to an unsupported assumption ({assumption_string}).")
+
+ def resolve(expr):
+ if not expr.args:
+ return expr
+ args = []
+ for arg in expr.args:
+ if isinstance(arg, Rational):
+ a = arg.numerator()
+ b = arg.denominator()
+ arg = k(a) / k(b)
+ else:
+ arg = resolve(arg)
+ args.append(arg)
+ return expr.func(*args)
+
+ expr = resolve(expr)
poly = Poly(expr, symbols(param), domain=k)
roots = poly.ground_roots()
for root in roots.keys():
@@ -160,6 +169,19 @@ class Formula(ABC):
break
else:
raise UnsatisfiedAssumptionError(f"Unsatisfied assumption in the formula ({assumption_string}).")
+
+ def __call__(self, *points: Any, **params: Mod) -> Tuple[Any, ...]:
+ """
+ Execute a formula.
+
+ :param points: Points to pass into the formula.
+ :param params: Parameters of the curve.
+ :return: The resulting point(s).
+ """
+ from .point import Point
+ self.__validate_points(points, params)
+ field = int(params[next(iter(params.keys()))].n) # TODO: This is nasty...
+ self.__validate_assumptions(field, params)
# Execute the actual formula.
with FormulaAction(self, *points, **params) as action:
for op in self.code:
diff --git a/test/ec/test_regress.py b/test/ec/test_regress.py
index 03e6afd..e2c1799 100644
--- a/test/ec/test_regress.py
+++ b/test/ec/test_regress.py
@@ -1,8 +1,12 @@
from unittest import TestCase
+from pyecsca.ec.coordinates import AffineCoordinateModel
+from pyecsca.ec.curve import EllipticCurve
from pyecsca.ec.mod import Mod
+from pyecsca.ec.model import MontgomeryModel, EdwardsModel
from pyecsca.ec.params import get_params
from pyecsca.ec.mult import LTRMultiplier
+from pyecsca.ec.point import Point
class RegressionTests(TestCase):
@@ -34,3 +38,14 @@ class RegressionTests(TestCase):
affine_triple = e222.curve.affine_add(affine_base, affine_double)
self.assertIsNotNone(affine_double)
self.assertIsNotNone(affine_triple)
+
+ def test_issue_9(self):
+ model = MontgomeryModel()
+ coords = model.coordinates["xz"]
+ p = 19
+ neutral = Point(coords, X=Mod(1, p), Z=Mod(0, p))
+ curve = EllipticCurve(model, coords, p, neutral, {"a": Mod(8, p), "b": Mod(1, p)})
+ base = Point(coords, X=Mod(12, p), Z=Mod(1, p))
+ formula = coords.formulas["dbl-1987-m-2"]
+ res = formula(base, **curve.parameters)[0]
+ self.assertIsNotNone(res)