diff options
Diffstat (limited to 'pyecsca/ec')
| -rw-r--r-- | pyecsca/ec/coordinates.py | 23 | ||||
| -rw-r--r-- | pyecsca/ec/formula.py | 26 | ||||
| -rw-r--r-- | pyecsca/ec/op.py | 17 |
3 files changed, 60 insertions, 6 deletions
diff --git a/pyecsca/ec/coordinates.py b/pyecsca/ec/coordinates.py index 9b63c75..fe55d42 100644 --- a/pyecsca/ec/coordinates.py +++ b/pyecsca/ec/coordinates.py @@ -1,5 +1,6 @@ """Provides a coordinate model class.""" from ast import parse, Module +from astunparse import unparse from importlib_resources.abc import Traversable from importlib_resources import as_file from typing import List, Any, MutableMapping @@ -17,6 +18,7 @@ from .formula import ( ScalingEFDFormula, NegationEFDFormula, ) +from ..misc.utils import pexec @public @@ -52,6 +54,23 @@ class CoordinateModel: def __repr__(self): return f'{self.__class__.__name__}("{self.name}" on {self.curve_model.name})' + def __getstate__(self): + state = self.__dict__.copy() + state["satisfying"] = list(map(unparse, state["satisfying"])) + state["toaffine"] = list(map(unparse, state["toaffine"])) + state["tosystem"] = list(map(unparse, state["tosystem"])) + state["assumptions"] = list(map(unparse, state["assumptions"])) + state["neutral"] = list(map(unparse, state["neutral"])) + return state + + def __setstate__(self, state): + state["satisfying"] = list(map(pexec, state["satisfying"])) + state["toaffine"] = list(map(pexec, state["toaffine"])) + state["tosystem"] = list(map(pexec, state["tosystem"])) + state["assumptions"] = list(map(pexec, state["assumptions"])) + state["neutral"] = list(map(pexec, state["neutral"])) + self.__dict__.update(state) + @public class AffineCoordinateModel(CoordinateModel): @@ -115,7 +134,9 @@ class EFDCoordinateModel(CoordinateModel): "negation": NegationEFDFormula, } cls = formula_types.get(formula_type, EFDFormula) - self.formulas[fpath.stem] = cls(fpath, fpath.with_suffix(".op3"), fpath.stem, self) + self.formulas[fpath.stem] = cls( + fpath, fpath.with_suffix(".op3"), fpath.stem, self + ) def __read_coordinates_file(self, file_path: Traversable): with file_path.open("rb") as f: diff --git a/pyecsca/ec/formula.py b/pyecsca/ec/formula.py index ed3afb5..91e551b 100644 --- a/pyecsca/ec/formula.py +++ b/pyecsca/ec/formula.py @@ -18,6 +18,7 @@ from .error import UnsatisfiedAssumptionError, raise_unsatisified_assumption from .mod import Mod, SymbolicMod from .op import CodeOp, OpType from ..misc.cfg import getconfig +from ..misc.utils import peval @public @@ -31,7 +32,9 @@ class OpResult: def __init__(self, name: str, value: Mod, op: OpType, *parents: Any): if len(parents) != op.num_inputs: - raise ValueError(f"Wrong number of parents ({len(parents)}) to OpResult: {op} ({op.num_inputs}).") + raise ValueError( + f"Wrong number of parents ({len(parents)}) to OpResult: {op} ({op.num_inputs})." + ) self.parents = tuple(parents) self.name = name self.value = value @@ -156,7 +159,9 @@ class Formula(ABC): # Validate assumptions and compute formula parameters. # TODO: Should this also validate coordinate assumptions and compute their parameters? is_symbolic = any(isinstance(x, SymbolicMod) for x in params.values()) - for assumption, assumption_string in zip(self.assumptions, self.assumptions_str): + for assumption, assumption_string in zip( + self.assumptions, self.assumptions_str + ): lhs, rhs = assumption_string.split(" == ") if lhs in params: # Handle an assumption check on value of input points. @@ -275,6 +280,15 @@ class Formula(ABC): def __repr__(self): return f"{self.__class__.__name__}({self.name} for {self.coordinate_model})" + def __getstate__(self): + state = self.__dict__.copy() + state["assumptions"] = list(map(unparse, state["assumptions"])) + return state + + def __setstate__(self, state): + state["assumptions"] = list(map(peval, state["assumptions"])) + self.__dict__.update(state) + @property @abstractmethod def input_index(self): @@ -340,7 +354,13 @@ class Formula(ABC): class EFDFormula(Formula): """Formula from the [EFD]_.""" - def __init__(self, meta_path: Traversable, op3_path: Traversable, name: str, coordinate_model: Any): + def __init__( + self, + meta_path: Traversable, + op3_path: Traversable, + name: str, + coordinate_model: Any, + ): self.name = name self.coordinate_model = coordinate_model self.meta = {} diff --git a/pyecsca/ec/op.py b/pyecsca/ec/op.py index 8841f2c..2a730e1 100644 --- a/pyecsca/ec/op.py +++ b/pyecsca/ec/op.py @@ -15,11 +15,13 @@ from ast import ( operator as ast_operator, unaryop as ast_unaryop, USub, + parse, ) from enum import Enum from types import CodeType from typing import FrozenSet, cast, Any, Optional, Union, Tuple +from astunparse import unparse from public import public from .mod import Mod @@ -131,12 +133,23 @@ class CodeOp: @property def parents(self) -> Tuple[Union[str, int]]: if self.operator in (OpType.Inv, OpType.Neg): - return self.right, # type: ignore + return (self.right,) # type: ignore elif self.operator in (OpType.Sqr, OpType.Id): - return self.left, # type: ignore + return (self.left,) # type: ignore else: return self.left, self.right # type: ignore + def __getstate__(self): + state = self.__dict__.copy() + state["code"] = unparse(state["code"]) + del state["compiled"] + return state + + def __setstate__(self, state): + state["code"] = parse(state["code"], mode="exec") + state["compiled"] = compile(state["code"], "", mode="exec") + self.__dict__.update(state) + def __str__(self): return f"{self.result} = {self.left if self.left is not None else ''}{self.operator.op_str}{self.right if self.right is not None else ''}" |
