diff options
| -rw-r--r-- | pyecsca/ec/mult/naf.py | 2 | ||||
| -rw-r--r-- | pyecsca/misc/cfg.py | 9 | ||||
| -rw-r--r-- | pyecsca/misc/utils.py | 28 | ||||
| -rw-r--r-- | pyecsca/sca/re/rpa.py | 52 |
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 |
