diff options
| author | J08nY | 2021-01-06 15:47:58 +0100 |
|---|---|---|
| committer | J08nY | 2021-01-06 15:47:58 +0100 |
| commit | 9dd00af7aede2085812a65a888a7539e9a792213 (patch) | |
| tree | 03ebbc2fbcf6b886e274049939e6c3a98e65e430 | |
| parent | 9a3bc0379b5033e03dec6473fa645c4bf1770105 (diff) | |
| download | pyecsca-9dd00af7aede2085812a65a888a7539e9a792213.tar.gz pyecsca-9dd00af7aede2085812a65a888a7539e9a792213.tar.zst pyecsca-9dd00af7aede2085812a65a888a7539e9a792213.zip | |
Resolve out rationals introduced by SymPy.
Fixes #9.
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | pyecsca/ec/formula.py | 46 | ||||
| -rw-r--r-- | test/ec/test_regress.py | 15 |
3 files changed, 50 insertions, 13 deletions
@@ -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) |
