aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pyecsca/ec/formula/efd.py3
-rw-r--r--pyecsca/sca/re/rpa.py55
-rw-r--r--pyecsca/sca/re/tree.py63
3 files changed, 68 insertions, 53 deletions
diff --git a/pyecsca/ec/formula/efd.py b/pyecsca/ec/formula/efd.py
index 4e3a82d..485e6f5 100644
--- a/pyecsca/ec/formula/efd.py
+++ b/pyecsca/ec/formula/efd.py
@@ -1,7 +1,4 @@
""""""
-from functools import cached_property
-from itertools import product
-
from public import public
from importlib_resources.abc import Traversable
diff --git a/pyecsca/sca/re/rpa.py b/pyecsca/sca/re/rpa.py
index b1661e6..6d2218b 100644
--- a/pyecsca/sca/re/rpa.py
+++ b/pyecsca/sca/re/rpa.py
@@ -3,13 +3,13 @@ Provides functionality inspired by the Refined-Power Analysis attack by Goubin [
"""
from copy import copy, deepcopy
-from anytree import Node, RenderTree
+from anytree import RenderTree
from public import public
-from typing import MutableMapping, Optional, Callable, List, Set
-from collections import Counter
+from typing import MutableMapping, Optional, Callable, List
from sympy import FF, sympify, Poly, symbols
+from .tree import build_distinguishing_tree
from ...ec.coordinates import AffineCoordinateModel
from ...ec.formula import (
FormulaAction,
@@ -149,51 +149,6 @@ def rpa_input_point(k: Mod, rpa_point: Point, params: DomainParameters) -> Point
return params.curve.affine_multiply(rpa_point, int(kinv))
-def build_distinguishing_tree(mults_to_multiples: MutableMapping[ScalarMultiplier, Set[Mod]],
- **kwargs) -> Optional[Node]:
- """
- Build an RPA distinguishing tree for a given mults-to-multiples mapping (induced by some fixed scalar).
-
- :param mults_to_multiples:
- :param kwargs:
- :return:
- """
- n_mults = len(mults_to_multiples)
- # If there is only one remaining multiple, put a single
- if n_mults == 1:
- return Node(None, mults=list(mults_to_multiples.keys()), **kwargs)
-
- counts: Counter = Counter()
- for multiples in mults_to_multiples.values():
- counts.update(multiples)
-
- nhalf = n_mults / 2
- best_distinguishing_multiple = None
- best_count = None
- best_nhalf_distance = None
- for multiple, count in counts.items():
- if best_distinguishing_multiple is None or abs(count - nhalf) < best_nhalf_distance:
- best_distinguishing_multiple = multiple
- best_count = count
- best_nhalf_distance = abs(count - nhalf)
-
- if best_count in (0, n_mults, None):
- return Node(None, mults=list(mults_to_multiples.keys()), **kwargs)
-
- result = Node(best_distinguishing_multiple, mults=list(mults_to_multiples.keys()), **kwargs)
- true_mults = {mult: multiples for mult, multiples in mults_to_multiples.items() if
- best_distinguishing_multiple in multiples}
- true_child = build_distinguishing_tree(true_mults, oracle_response=True)
- if true_child:
- true_child.parent = result
- false_mults = {mult: multiples for mult, multiples in mults_to_multiples.items() if
- best_distinguishing_multiple not in multiples}
- false_child = build_distinguishing_tree(false_mults, oracle_response=False)
- if false_child:
- false_child.parent = result
- return result
-
-
@public
def rpa_distinguish(params: DomainParameters, mults: List[ScalarMultiplier], oracle: Callable[[int, Point], bool],
bound: Optional[int] = None, majority: int = 1, use_init: bool = True, use_multiply: bool = True) -> List[ScalarMultiplier]:
@@ -263,7 +218,7 @@ def rpa_distinguish(params: DomainParameters, mults: List[ScalarMultiplier], ora
tree = build_distinguishing_tree(mults_to_multiples)
log("Built distinguishing tree.")
- log(RenderTree(tree).by_attr(lambda n: n.name if n.name else [mult.__class__.__name__ for mult in n.mults]))
+ log(RenderTree(tree).by_attr(lambda n: n.name if n.name else [mult.__class__.__name__ for mult in n.cfgs]))
if tree is None or not tree.children:
tries += 1
continue
@@ -285,7 +240,7 @@ def rpa_distinguish(params: DomainParameters, mults: List[ScalarMultiplier], ora
log(mult.__class__.__name__, best_distinguishing_multiple in mults_to_multiples[mult])
response_map = {child.oracle_response: child for child in current_node.children}
current_node = response_map[response]
- mults = current_node.mults
+ mults = current_node.cfgs
log([mult.__class__.__name__ for mult in mults])
log()
diff --git a/pyecsca/sca/re/tree.py b/pyecsca/sca/re/tree.py
new file mode 100644
index 0000000..d3433cc
--- /dev/null
+++ b/pyecsca/sca/re/tree.py
@@ -0,0 +1,63 @@
+from typing import Mapping, Any, Set
+from collections import Counter
+from public import public
+from anytree import Node
+
+
+@public
+def build_distinguishing_tree(
+ cfg2resp: Mapping[Any, Set[Any]], **kwargs
+) -> Node:
+ """
+ Build a distinguishing tree for a given mapping of configs to True oracle responses.
+
+ :param cfg2resp:
+ :param kwargs:
+ :return:
+ """
+ n_cfgs = len(cfg2resp)
+ # If there is only one remaining cfg, we do not need to continue and just return (base case 1).
+ # Note that n_cfgs will never be 0 here, as the base case 2 returns if the cfgs cannot be split into two sets (one would be empty).
+ if n_cfgs == 1:
+ return Node(None, cfgs=list(cfg2resp.keys()), **kwargs)
+
+ counts: Counter = Counter()
+ for elements in cfg2resp.values(): # Elements of the set need to be hashable
+ counts.update(elements)
+
+ nhalf = n_cfgs / 2
+ best_distinguishing_element = None
+ best_count = None
+ best_nhalf_distance = None
+ for multiple, count in counts.items():
+ if (
+ best_distinguishing_element is None
+ or abs(count - nhalf) < best_nhalf_distance
+ ):
+ best_distinguishing_element = multiple
+ best_count = count
+ best_nhalf_distance = abs(count - nhalf)
+
+ # We found nothing distinguishing the configs, so return them all (base case 2).
+ if best_count in (0, n_cfgs, None):
+ return Node(None, cfgs=list(cfg2resp.keys()), **kwargs)
+
+ result = Node(
+ best_distinguishing_element, cfgs=list(cfg2resp.keys()), **kwargs
+ )
+ # Now go deeper and split based on the best-distinguishing element.
+ true_cfgs = {
+ mult: elements
+ for mult, elements in cfg2resp.items()
+ if best_distinguishing_element in elements
+ }
+ true_child = build_distinguishing_tree(true_cfgs, oracle_response=True)
+ true_child.parent = result
+ false_cfgs = {
+ mult: elements
+ for mult, elements in cfg2resp.items()
+ if best_distinguishing_element not in elements
+ }
+ false_child = build_distinguishing_tree(false_cfgs, oracle_response=False)
+ false_child.parent = result
+ return result