aboutsummaryrefslogtreecommitdiffhomepage
path: root/pyecsca/ec/formula/efd.py
diff options
context:
space:
mode:
authorJ08nY2023-12-05 13:58:51 +0100
committerJ08nY2023-12-05 14:06:07 +0100
commit3860a7ca356e334e4a5c7419e774e49f79408905 (patch)
tree8ebdbe1188a2c52f0a034ceacf9276d652c55dca /pyecsca/ec/formula/efd.py
parentfc610e310c649d275b43df92cf127ab07d4663b2 (diff)
downloadpyecsca-3860a7ca356e334e4a5c7419e774e49f79408905.tar.gz
pyecsca-3860a7ca356e334e4a5c7419e774e49f79408905.tar.zst
pyecsca-3860a7ca356e334e4a5c7419e774e49f79408905.zip
Integrate formula expansion.
- Moved into pyecsca.ec.formula subpackage. - Unified formula metrics. - Moved tests into tests with library_formula fixture. - Cleaned up typing.
Diffstat (limited to 'pyecsca/ec/formula/efd.py')
-rw-r--r--pyecsca/ec/formula/efd.py142
1 files changed, 142 insertions, 0 deletions
diff --git a/pyecsca/ec/formula/efd.py b/pyecsca/ec/formula/efd.py
new file mode 100644
index 0000000..0156fb3
--- /dev/null
+++ b/pyecsca/ec/formula/efd.py
@@ -0,0 +1,142 @@
+""""""
+from functools import cached_property
+from itertools import product
+
+from public import public
+
+from importlib_resources.abc import Traversable
+from typing import Any
+from .base import (
+ Formula,
+ CodeOp,
+ AdditionFormula,
+ DoublingFormula,
+ TriplingFormula,
+ NegationFormula,
+ ScalingFormula,
+ DifferentialAdditionFormula,
+ LadderFormula,
+)
+from ast import parse
+
+
+class EFDFormula(Formula):
+ """Formula from the [EFD]_."""
+
+ def __init__(
+ self,
+ meta_path: Traversable,
+ op3_path: Traversable,
+ name: str,
+ coordinate_model: Any,
+ ):
+ self.name = name
+ self.coordinate_model = coordinate_model
+ self.meta = {}
+ self.parameters = []
+ self.assumptions = []
+ self.code = []
+ self.unified = False
+ self.__read_meta_file(meta_path)
+ self.__read_op3_file(op3_path)
+
+ def __read_meta_file(self, path: Traversable):
+ with path.open("rb") as f:
+ line = f.readline().decode("ascii").rstrip()
+ while line:
+ if line.startswith("source"):
+ self.meta["source"] = line[7:]
+ elif line.startswith("parameter"):
+ self.parameters.append(line[10:])
+ elif line.startswith("assume"):
+ self.assumptions.append(
+ parse(
+ line[7:].replace("=", "==").replace("^", "**"), mode="eval"
+ )
+ )
+ elif line.startswith("unified"):
+ self.unified = True
+ line = f.readline().decode("ascii").rstrip()
+
+ def __read_op3_file(self, path: Traversable):
+ with path.open("rb") as f:
+ for line in f.readlines():
+ code_module = parse(
+ line.decode("ascii").replace("^", "**"), str(path), mode="exec"
+ )
+ self.code.append(CodeOp(code_module))
+
+ def __str__(self):
+ return f"{self.coordinate_model!s}/{self.name}"
+
+ @cached_property
+ def input_index(self):
+ return 1
+
+ @cached_property
+ def output_index(self):
+ return max(self.num_inputs + 1, 3)
+
+ @cached_property
+ def inputs(self):
+ return {
+ var + str(i)
+ for var, i in product(
+ self.coordinate_model.variables, range(1, 1 + self.num_inputs)
+ )
+ }
+
+ @cached_property
+ def outputs(self):
+ return {
+ var + str(i)
+ for var, i in product(
+ self.coordinate_model.variables,
+ range(self.output_index, self.output_index + self.num_outputs),
+ )
+ }
+
+ def __eq__(self, other):
+ if not isinstance(other, EFDFormula):
+ return False
+ return (
+ self.name == other.name and self.coordinate_model == other.coordinate_model
+ )
+
+ def __hash__(self):
+ return hash((self.coordinate_model, self.name))
+
+
+@public
+class AdditionEFDFormula(AdditionFormula, EFDFormula):
+ pass
+
+
+@public
+class DoublingEFDFormula(DoublingFormula, EFDFormula):
+ pass
+
+
+@public
+class TriplingEFDFormula(TriplingFormula, EFDFormula):
+ pass
+
+
+@public
+class NegationEFDFormula(NegationFormula, EFDFormula):
+ pass
+
+
+@public
+class ScalingEFDFormula(ScalingFormula, EFDFormula):
+ pass
+
+
+@public
+class DifferentialAdditionEFDFormula(DifferentialAdditionFormula, EFDFormula):
+ pass
+
+
+@public
+class LadderEFDFormula(LadderFormula, EFDFormula):
+ pass