aboutsummaryrefslogtreecommitdiff
path: root/pyecsca/sca/re/epa.py
diff options
context:
space:
mode:
Diffstat (limited to 'pyecsca/sca/re/epa.py')
-rw-r--r--pyecsca/sca/re/epa.py78
1 files changed, 78 insertions, 0 deletions
diff --git a/pyecsca/sca/re/epa.py b/pyecsca/sca/re/epa.py
new file mode 100644
index 0000000..14c7206
--- /dev/null
+++ b/pyecsca/sca/re/epa.py
@@ -0,0 +1,78 @@
+"""
+Provides functionality inspired by the Exceptional Procedure Attack [EPA]_.
+"""
+
+from typing import Type, Callable, Literal
+
+from public import public
+
+from pyecsca.ec.context import local
+from pyecsca.ec.formula.fake import FakePoint
+from pyecsca.ec.mult import ScalarMultiplier
+from pyecsca.ec.mult.fake import cached_fake_mult
+from pyecsca.ec.params import DomainParameters
+from pyecsca.sca.re.rpa import MultipleContext
+
+
+@public
+def errors_out(
+ scalar: int,
+ params: DomainParameters,
+ mult_class: Type[ScalarMultiplier],
+ mult_factory: Callable,
+ check_funcs: dict[str, Callable],
+ check_condition: Literal["all"] | Literal["necessary"],
+ precomp_to_affine: bool,
+) -> bool:
+ """
+
+ :param scalar:
+ :param params:
+ :param mult_class:
+ :param mult_factory:
+ :param check_funcs:
+ :param check_condition:
+ :param precomp_to_affine:
+ :return:
+
+ .. note::
+ The scalar multiplier must not short-circuit.
+ """
+ mult = cached_fake_mult(mult_class, mult_factory, params)
+ ctx = MultipleContext(keep_base=True)
+ with local(ctx, copy=False):
+ mult.init(params, FakePoint(params.curve.coordinate_model))
+
+ with local(ctx, copy=False):
+ out = mult.multiply(scalar)
+
+ affine_points = {out, *ctx.precomp.values()} if precomp_to_affine else {out}
+ if check_condition == "all":
+ points = set(ctx.points.keys())
+ elif check_condition == "necessary":
+ points = set(affine_points)
+ queue = set(affine_points)
+ while queue:
+ point = queue.pop()
+ for parent in ctx.parents[point]:
+ points.add(parent)
+ queue.add(parent)
+ else:
+ raise ValueError("check_condition must be 'all' or 'necessary'")
+
+ # Special case the "to affine" transform and checks
+ # 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]))
+ if func(*inputs):
+ return True
+ return False