diff options
| author | J08nY | 2025-07-31 13:29:10 +0200 |
|---|---|---|
| committer | J08nY | 2025-07-31 13:29:10 +0200 |
| commit | f3bfa81460187ec3f696d2a851266cba46563a32 (patch) | |
| tree | 3463d4d743ff0198d810278e6bacf3d0abb5f6d9 | |
| parent | e39369d6aa28c85b04aab98034b6d2a2a648ea5c (diff) | |
| download | pyecsca-f3bfa81460187ec3f696d2a851266cba46563a32.tar.gz pyecsca-f3bfa81460187ec3f696d2a851266cba46563a32.tar.zst pyecsca-f3bfa81460187ec3f696d2a851266cba46563a32.zip | |
| -rw-r--r-- | pyecsca/ec/formula/fake.py | 10 | ||||
| -rw-r--r-- | pyecsca/sca/re/epa.py | 88 | ||||
| -rw-r--r-- | test/sca/test_epa.py | 24 |
3 files changed, 104 insertions, 18 deletions
diff --git a/pyecsca/ec/formula/fake.py b/pyecsca/ec/formula/fake.py index c85f607..f3f0c32 100644 --- a/pyecsca/ec/formula/fake.py +++ b/pyecsca/ec/formula/fake.py @@ -95,10 +95,20 @@ class FakePoint(Point): def field(self): return 0 + @field.setter + def field(self, value): + """Setter for field, does nothing.""" + pass + @property def coords(self): return _fake_coords(self.coordinate_model) + @coords.setter + def coords(self, value): + """Setter for coordinates, does nothing.""" + pass + def __str__(self): return f"FakePoint{id(self)}" diff --git a/pyecsca/sca/re/epa.py b/pyecsca/sca/re/epa.py index 32abb45..325f11d 100644 --- a/pyecsca/sca/re/epa.py +++ b/pyecsca/sca/re/epa.py @@ -11,21 +11,23 @@ from pyecsca.sca.re.rpa import MultipleContext @public -def errors_out( +def graph_to_check_inputs( ctx: MultipleContext, out: Point, - check_funcs: dict[str, Callable], check_condition: Union[Literal["all"], Literal["necessary"]], precomp_to_affine: bool, -) -> bool: +) -> dict[str, list[tuple[int, ...]]]: """ + Compute the inputs for the checks based on the context and output point. This function traverses the graph of points + and collects the inputs for each formula type, in the form of tuples of input multiples. It also adds a special + "affine" formula that checks the multiples of the points that need to be converted to affine coordinates. Which + points this "affine" formula checks depends on the `precomp_to_affine` parameter. :param ctx: The context containing the points and formulas. - :param out: The output point to check. - :param check_funcs: - :param check_condition: - :param precomp_to_affine: - :return: + :param out: The output point of the computation. + :param check_condition: Whether to check all points or only those necessary for the output point. + :param precomp_to_affine: Whether to include the precomputed points in the to-affine checks. + :return: A dictionary mapping formula names to lists of tuples of input multiples. .. note:: The scalar multiplier must not short-circuit. @@ -43,20 +45,72 @@ def errors_out( queue.add(parent) else: raise ValueError("check_condition must be 'all' or 'necessary'") - # Special case the "to affine" transform and checks + formula_checks: dict[str, list[tuple[int, ...]]] = { + "affine": [(ctx.points[point],) for point in affine_points] + } # This actually passes the multiple itself to the check, not the inputs(parents) - for point in affine_points: - if "affine" in check_funcs: - func = check_funcs["affine"] - if func(ctx.points[point]): - return True # Now handle the regular checks for point in points: formula = ctx.formulas[point] - if formula in check_funcs: - func = check_funcs[formula] - inputs = list(map(lambda pt: ctx.points[pt], ctx.parents[point])) + inputs = tuple(map(lambda pt: ctx.points[pt], ctx.parents[point])) + check_list = formula_checks.setdefault(formula, []) + check_list.append(inputs) + return formula_checks + + +@public +def evaluate_checks( + check_funcs: dict[str, Callable], check_inputs: dict[str, list[tuple[int, ...]]] +) -> bool: + """ + Evaluate the checks for each formula type based on the provided functions and inputs. + + :param check_funcs: The functions to apply for each formula type. There are two callable types: + - `check(k, l, q)`, that gets applied to binary formulas (like `add`), where `k` and `l` are the input multiples + of the base point and `q` is the base point order. + - `check(k, q)`, that gets applied to unary formulas (like conversion to affine `affine`), where `k` is the input multiple + of the base point and `q` is the base point order. + :param check_inputs: A dictionary mapping formula names to lists of tuples of input multiples. The output of + :func:`graph_to_check_inputs`. + :return: Whether any of the checks returned True -> whether the computation errors out. + + .. note:: + The scalar multiplier must not short-circuit. + """ + for name, func in check_funcs.items(): + if name not in check_inputs: + continue + for inputs in check_inputs[name]: if func(*inputs): return True return False + + +@public +def errors_out( + ctx: MultipleContext, + out: Point, + check_funcs: dict[str, Callable], + check_condition: Union[Literal["all"], Literal["necessary"]], + precomp_to_affine: bool, +) -> bool: + """ + Check whether the computation errors out based on the provided context, output point, and check functions. + + :param ctx: The context containing the points and formulas. + :param out: The output point of the computation. + :param check_funcs: The functions to apply for each formula type. There are two callable types: + - `check(k, l, q)`, that gets applied to binary formulas (like `add`), where `k` and `l` are the input multiples + of the base point and `q` is the base point order. + - `check(k, q)`, that gets applied to unary formulas (like conversion to affine `affine`), where `k` is the input multiple + of the base point and `q` is the base point order. + :param check_condition: Whether to check all points or only those necessary for the output point. + :param precomp_to_affine: Whether to include the precomputed points in the to-affine checks. + :return: Whether any of the checks returned True -> whether the computation errors out. + + .. note:: + The scalar multiplier must not short-circuit. + """ + formula_checks = graph_to_check_inputs(ctx, out, check_condition, precomp_to_affine) + return evaluate_checks(check_funcs, formula_checks) diff --git a/test/sca/test_epa.py b/test/sca/test_epa.py index 4bb27f0..6f4afbb 100644 --- a/test/sca/test_epa.py +++ b/test/sca/test_epa.py @@ -1,7 +1,10 @@ from functools import partial +import pytest + +from pyecsca.ec.coordinates import EFDCoordinateModel from pyecsca.ec.mult import LTRMultiplier, CombMultiplier -from pyecsca.sca import multiple_graph +from pyecsca.sca.re.rpa import multiple_graph from pyecsca.sca.re.epa import errors_out @@ -74,3 +77,22 @@ def test_errors_out_comb(secp128r1): precomp_to_affine=False, ) assert not res_check_k_no_affine_precomp + + +@pytest.mark.skip(reason="Debug only") +def test_memory_consumption(secp128r1): + ctx, out = multiple_graph( + scalar=2**127 + 12127486321, + params=secp128r1, + mult_class=LTRMultiplier, + mult_factory=LTRMultiplier, + ) + try: + from pympler.asizeof import Asizer + + sizer = Asizer() + sizer.exclude_types(EFDCoordinateModel) + print(sizer.asized(ctx, detail=2).format()) + print(sizer.asized(out, detail=2).format()) + except ImportError: + pass |
