diff options
| -rw-r--r-- | pyecsca/ec/formula.py | 19 | ||||
| -rw-r--r-- | pyecsca/ec/op.py | 13 | ||||
| -rw-r--r-- | test/sca/test_leakage_models.py | 33 |
3 files changed, 55 insertions, 10 deletions
diff --git a/pyecsca/ec/formula.py b/pyecsca/ec/formula.py index 226734d..317788b 100644 --- a/pyecsca/ec/formula.py +++ b/pyecsca/ec/formula.py @@ -29,6 +29,8 @@ class OpResult: value: Mod 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}).") self.parents = tuple(parents) self.name = name self.value = value @@ -56,7 +58,7 @@ class FormulaAction(ResultAction): intermediates: MutableMapping[str, List[OpResult]] """Intermediates computed during execution.""" op_results: List[OpResult] - """""" + """The intermediates but ordered as they were computed.""" outputs: MutableMapping[str, OpResult] """The output variables.""" output_points: List[Any] @@ -73,12 +75,15 @@ class FormulaAction(ResultAction): self.output_points = [] def add_operation(self, op: CodeOp, value: Mod): - parents: List[Union[Mod, OpResult]] = [] - for parent in {*op.variables, *op.parameters}: - if parent in self.intermediates: - parents.append(self.intermediates[parent][-1]) - elif parent in self.inputs: - parents.append(self.inputs[parent]) + parents: List[Union[int, Mod, OpResult]] = [] + for parent in op.parents: + if isinstance(parent, str): + if parent in self.intermediates: + parents.append(self.intermediates[parent][-1]) + elif parent in self.inputs: + parents.append(self.inputs[parent]) + else: + parents.append(parent) result = OpResult(op.result, value, op.operator, *parents) li = self.intermediates.setdefault(op.result, []) li.append(result) diff --git a/pyecsca/ec/op.py b/pyecsca/ec/op.py index d656bc3..b83562c 100644 --- a/pyecsca/ec/op.py +++ b/pyecsca/ec/op.py @@ -18,7 +18,7 @@ from ast import ( ) from enum import Enum from types import CodeType -from typing import FrozenSet, cast, Any, Optional, Union +from typing import FrozenSet, cast, Any, Optional, Union, Tuple from public import public @@ -54,6 +54,8 @@ class CodeOp: """The parameters used in the operation (e.g. `a`, `b`).""" variables: FrozenSet[str] """The variables used in the operation (e.g. `X1`, `Z2`).""" + constants: FrozenSet[int] # TODO: Might not be only int? See issue in Formula eval. + """The constants used in the operation.""" code: Module """The code of the operation.""" operator: OpType @@ -126,6 +128,15 @@ class CodeOp: return OpType.Pow return OpType.Id + @property + def parents(self) -> Tuple[Union[str, int]]: + if self.operator == OpType.Inv or self.operator == OpType.Neg: + return self.right, # type: ignore + elif self.operator == OpType.Sqr or self.operator == OpType.Id: + return self.left, # type: ignore + else: + return self.left, self.right # type: ignore + 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 ''}" diff --git a/test/sca/test_leakage_models.py b/test/sca/test_leakage_models.py index d1e6fd1..e9da42c 100644 --- a/test/sca/test_leakage_models.py +++ b/test/sca/test_leakage_models.py @@ -1,9 +1,10 @@ from unittest import TestCase from pyecsca.ec.context import local, DefaultContext -from pyecsca.ec.formula import FormulaAction +from pyecsca.ec.formula import FormulaAction, OpResult from pyecsca.ec.mod import Mod from pyecsca.ec.mult import LTRMultiplier +from pyecsca.ec.op import OpType from pyecsca.ec.params import get_params from pyecsca.sca.attack.leakage_model import Identity, Bit, Slice, HammingWeight, HammingDistance, BitLength @@ -63,7 +64,7 @@ class ModelTraceTests(TestCase): self.neg = self.coords.formulas["neg"] self.scale = self.coords.formulas["z"] - def test_mult(self): + def test_mult_hw(self): scalar = 0x123456789 mult = LTRMultiplier( self.add, @@ -88,3 +89,31 @@ class ModelTraceTests(TestCase): ctx.actions.walk(callback) self.assertGreater(len(trace), 0) + + def test_mult_hd(self): + scalar = 0x123456789 + mult = LTRMultiplier( + self.add, + self.dbl, + self.scale, + always=True, + complete=False, + short_circuit=True, + ) + with local(DefaultContext()) as ctx: + mult.init(self.secp128r1, self.base) + mult.multiply(scalar) + + lm = HammingDistance() + trace = [] + + def callback(action): + if isinstance(action, FormulaAction): + for intermediate in action.op_results: + if intermediate.op == OpType.Mult: + values = list(map(lambda v: v.value if isinstance(v, OpResult) else v, intermediate.parents)) + leak = lm(*values) + trace.append(leak) + + ctx.actions.walk(callback) + self.assertGreater(len(trace), 0) |
