aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pyecsca/ec/mult/naf.py2
-rw-r--r--pyecsca/misc/cfg.py9
-rw-r--r--pyecsca/misc/utils.py28
-rw-r--r--pyecsca/sca/re/rpa.py52
4 files changed, 76 insertions, 15 deletions
diff --git a/pyecsca/ec/mult/naf.py b/pyecsca/ec/mult/naf.py
index c7dfa4a..84019b5 100644
--- a/pyecsca/ec/mult/naf.py
+++ b/pyecsca/ec/mult/naf.py
@@ -150,7 +150,7 @@ class WindowNAFMultiplier(AccumulatorMultiplier, ScalarMultiplier):
return self.formulas == other.formulas and self.short_circuit == other.short_circuit and self.width == other.width and self.precompute_negation == other.precompute_negation and self.accumulation_order == other.accumulation_order
def __repr__(self):
- return f"{self.__class__.__name__}({', '.join(map(str, self.formulas.values()))}, short_circuit={self.short_circuit}, precompute_negation={self.precompute_negation}, accumulation_order={self.accumulation_order.name})"
+ return f"{self.__class__.__name__}({', '.join(map(str, self.formulas.values()))}, short_circuit={self.short_circuit}, width={self.width}, precompute_negation={self.precompute_negation}, accumulation_order={self.accumulation_order.name})"
def init(self, params: DomainParameters, point: Point):
with PrecomputationAction(params, point):
diff --git a/pyecsca/misc/cfg.py b/pyecsca/misc/cfg.py
index ee9c95d..f73eea9 100644
--- a/pyecsca/misc/cfg.py
+++ b/pyecsca/misc/cfg.py
@@ -121,6 +121,12 @@ class ECConfig:
raise ValueError("Bad Mod implementaiton, can be one of 'python', 'gmp' or 'symbolic'.")
self._mod_implementation = value
+@public
+class LoggingConfig:
+ """Logging configuration."""
+
+ enabled: bool = True
+ """Whether logging is enabled."""
@public
class Config:
@@ -128,9 +134,12 @@ class Config:
ec: ECConfig
"""Configuration for the :py:mod:`pyecsca.ec` package."""
+ log: LoggingConfig
+ """Logging configuration."""
def __init__(self):
self.ec = ECConfig()
+ self.log = LoggingConfig()
_config: ContextVar[Config] = ContextVar("config", default=Config())
diff --git a/pyecsca/misc/utils.py b/pyecsca/misc/utils.py
index 1e9e9a0..010bbd2 100644
--- a/pyecsca/misc/utils.py
+++ b/pyecsca/misc/utils.py
@@ -1,6 +1,9 @@
"""Just some utilities I promise."""
+import sys
from ast import parse
+from pyecsca.misc.cfg import getconfig
+
def pexec(s):
return parse(s, mode="exec")
@@ -8,3 +11,28 @@ def pexec(s):
def peval(s):
return parse(s, mode="eval")
+
+
+def in_notebook() -> bool:
+ """Test whether we are executing in Jupyter notebook."""
+ try:
+ from IPython import get_ipython
+ if 'IPKernelApp' not in get_ipython().config: # pragma: no cover
+ return False
+ except ImportError:
+ return False
+ except AttributeError:
+ return False
+ return True
+
+
+def log(*args, **kwargs):
+ """Log a message."""
+ if in_notebook() and getconfig().log.enabled:
+ print(*args, **kwargs)
+
+
+def warn(*args, **kwargs):
+ """Log a message."""
+ if in_notebook() and getconfig().log.enabled:
+ print(*args, **kwargs, file=sys.stderr)
diff --git a/pyecsca/sca/re/rpa.py b/pyecsca/sca/re/rpa.py
index 5459ff2..a7797bc 100644
--- a/pyecsca/sca/re/rpa.py
+++ b/pyecsca/sca/re/rpa.py
@@ -23,6 +23,7 @@ from ...ec.params import DomainParameters
from ...ec.model import ShortWeierstrassModel, MontgomeryModel
from ...ec.point import Point
from ...ec.context import Context, Action, local
+from ...misc.utils import log, warn
@public
@@ -126,7 +127,14 @@ def rpa_point_x0(params: DomainParameters) -> Optional[Point]:
@public
-def rpa_distinguish(params: DomainParameters, mults: List[ScalarMultiplier], oracle: Callable[[int, Point], bool]) -> List[ScalarMultiplier]:
+def rpa_input_point(k: Mod, rpa_point: Point, params: DomainParameters) -> Point:
+ """Construct an (affine) input point P that will lead to an RPA point [k]P."""
+ kinv = k.inverse()
+ return params.curve.affine_multiply(rpa_point, int(kinv))
+
+
+@public
+def rpa_distinguish(params: DomainParameters, mults: List[ScalarMultiplier], oracle: Callable[[int, Point], bool], bound: Optional[int] = None) -> List[ScalarMultiplier]:
"""
Distinguish the scalar multiplier used (from the possible :paramref:`~.rpa_distinguish.mults`) using
an [RPA]_ :paramref:`~.rpa_distinguish.oracle`.
@@ -134,25 +142,42 @@ def rpa_distinguish(params: DomainParameters, mults: List[ScalarMultiplier], ora
:param params: The domain parameters to use.
:param mults: The list of possible multipliers.
:param oracle: An oracle that returns `True` when an RPA point is encountered during scalar multiplication of the input by the scalar.
+ :param bound: A bound on the size of the scalar to consider.
:return: The list of possible multipliers after distinguishing (ideally just one).
"""
- P0 = rpa_point_0y(params) or rpa_point_x0(params)
+ # Try (x, 0) point first, as it is better for distinguishing (negatives).
+ if P0 := rpa_point_x0(params):
+ pm_equal = False
+ else:
+ P0 = rpa_point_0y(params)
+ pm_equal = True
+ warn("An (x, 0) RPA point could not be found. Distinguishing may be incomplete.")
if not P0:
raise ValueError("There are no RPA-points on the provided curve.")
- print(f"Got RPA point {P0}")
+ log(f"Got RPA point {P0}.")
+ if not bound:
+ bound = params.order
+ tries = 0
while True:
- scalar = int(Mod.random(params.order))
- print(f"Got scalar {scalar}")
- print([mult.__class__.__name__ for mult in mults])
+ tries += 1
+ if tries > 10:
+ warn("Tried more than 10 times. Aborting.")
+ return mults
+ scalar = int(Mod.random(bound))
+ log(f"Got scalar {scalar}")
+ log([mult.__class__.__name__ for mult in mults])
mults_to_multiples = {}
counts: Counter = Counter()
for mult in mults:
with local(MultipleContext()) as ctx:
mult.init(params, params.generator)
mult.multiply(scalar)
- multiples = set(ctx.points.values())
+ multiples = set(map(lambda v: Mod(v, params.order), ctx.points.values()))
+ if pm_equal:
+ multiples |= set(map(lambda v: -v, multiples))
mults_to_multiples[mult] = multiples
counts.update(multiples)
+ # TODO: Make this more noise resistant.
# TODO: This lower part can be repeated a few times for the same scalar above, which could reuse
# the computed multiples. Can be done until there is some distinguishing multiple.
@@ -166,20 +191,19 @@ def rpa_distinguish(params: DomainParameters, mults: List[ScalarMultiplier], ora
best_distinguishing_multiple = multiple
best_count = count
best_nhalf_distance = abs(count - nhalf)
- print(f"Chosen best distinguishing multiple {best_distinguishing_multiple} count={best_count} n={len(mults)}")
+ log(f"Chosen best distinguishing multiple {best_distinguishing_multiple} count={best_count} n={len(mults)}")
if best_count in (0, len(mults)):
continue
- multiple_inverse = Mod(best_distinguishing_multiple, params.order).inverse()
- P0_inverse = params.curve.affine_multiply(P0, int(multiple_inverse))
+ P0_inverse = rpa_input_point(best_distinguishing_multiple, P0, params)
response = oracle(scalar, P0_inverse)
- print(f"Oracle response -> {response}")
+ log(f"Oracle response -> {response}")
for mult in mults:
- print(mult.__class__.__name__, best_distinguishing_multiple in mults_to_multiples[mult])
+ log(mult.__class__.__name__, best_distinguishing_multiple in mults_to_multiples[mult])
filt = (lambda mult: best_distinguishing_multiple in mults_to_multiples[mult]) if response else (lambda mult: best_distinguishing_multiple not in mults_to_multiples[mult])
mults = list(filter(filt, mults))
- print([mult.__class__.__name__ for mult in mults])
- print()
+ log([mult.__class__.__name__ for mult in mults])
+ log()
if len(mults) == 1:
return mults