aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/_static/custom.css5
-rw-r--r--pyecsca/ec/countermeasures.py152
-rw-r--r--pyecsca/ec/mult/base.py2
-rw-r--r--pyecsca/sca/re/rpa.py10
-rw-r--r--test/sca/test_rpa.py6
5 files changed, 132 insertions, 43 deletions
diff --git a/docs/_static/custom.css b/docs/_static/custom.css
index ba69356..4c4e201 100644
--- a/docs/_static/custom.css
+++ b/docs/_static/custom.css
@@ -26,3 +26,8 @@ dt:target, .highlight {
width: 107px;
font-weight: normal !important;
}
+
+.frame mjx-math {
+ border: 1px solid #e0e0e0;
+ padding: 5px;
+} \ No newline at end of file
diff --git a/pyecsca/ec/countermeasures.py b/pyecsca/ec/countermeasures.py
index 5c52f35..8a479b6 100644
--- a/pyecsca/ec/countermeasures.py
+++ b/pyecsca/ec/countermeasures.py
@@ -7,7 +7,7 @@ from public import public
from pyecsca.ec.formula import AdditionFormula
from pyecsca.ec.mod import Mod, mod
-from pyecsca.ec.mult import ScalarMultiplier
+from pyecsca.ec.mult import ScalarMultiplier, ScalarMultiplicationAction
from pyecsca.ec.params import DomainParameters
from pyecsca.ec.point import Point
@@ -29,102 +29,180 @@ class ScalarMultiplierCountermeasure(ABC):
self.mult = mult
def init(self, params: DomainParameters, point: Point):
+ """Initialize the countermeasure with the parameters and the point."""
self.params = params
self.point = point
self.mult.init(self.params, self.point)
@abstractmethod
def multiply(self, scalar: int) -> Point:
+ """
+ Multiply the point with the scalar using the countermeasure.
+
+ .. note::
+ The countermeasure may compute multiple scalar multiplications internally.
+ Thus, it may call the init method of the scalar multiplier multiple times.
+ """
raise NotImplementedError
@public
class GroupScalarRandomization(ScalarMultiplierCountermeasure):
+ r"""
+ Group scalar randomization countermeasure.
+
+ Samples a random multiple, multiplies the order with it and adds it to the scalar.
+
+ .. math::
+ :class: frame
+
+ &r \xleftarrow{\$} \{0, 1, \ldots, 2^{\text{rand_bits}}\} \\
+ &\textbf{return}\ [k + r n]G
+
+ """
rand_bits: int
def __init__(self, mult: ScalarMultiplier, rand_bits: int = 32):
+ """
+ :param mult: The multiplier to use.
+ :param rand_bits: How many random bits to sample.
+ """
super().__init__(mult)
self.rand_bits = rand_bits
def multiply(self, scalar: int) -> Point:
if self.params is None or self.point is None:
raise ValueError("Not initialized.")
- order = self.params.order
- mask = int(Mod.random(1 << self.rand_bits))
- masked_scalar = scalar + mask * order
- return self.mult.multiply(masked_scalar)
+ with ScalarMultiplicationAction(self.point, self.params, scalar) as action:
+ order = self.params.order
+ mask = int(Mod.random(1 << self.rand_bits))
+ masked_scalar = scalar + mask * order
+ return action.exit(self.mult.multiply(masked_scalar))
@public
class AdditiveSplitting(ScalarMultiplierCountermeasure):
+ r"""
+ Additive splitting countermeasure.
+
+ Splits the scalar into two parts additively, multiplies the point with them and adds the results.
+
+ .. math::
+ :class: frame
+
+ &r \xleftarrow{\$} \{0, 1, \ldots, n\} \\
+ &\textbf{return}\ [k - r]G + [r]G
+
+ """
add: Optional[AdditionFormula]
def __init__(self, mult: ScalarMultiplier, add: Optional[AdditionFormula] = None):
+ """
+ :param mult: The multiplier to use.
+ :param add: Addition formula to use, if None, the formula from the multiplier is used.
+ """
super().__init__(mult)
self.add = add
def multiply(self, scalar: int) -> Point:
if self.params is None or self.point is None:
raise ValueError("Not initialized.")
-
- order = self.params.order
- r = Mod.random(order)
- s = scalar - r
- R = self.mult.multiply(int(r))
- S = self.mult.multiply(int(s))
- if self.add is None:
- return self.mult._add(R, S) # noqa: This is OK.
- else:
- return self.add(
- self.params.curve.prime, R, S, **self.params.curve.parameters
- )[0]
+ with ScalarMultiplicationAction(self.point, self.params, scalar) as action:
+ order = self.params.order
+ r = Mod.random(order)
+ s = scalar - r
+ R = self.mult.multiply(int(r))
+ S = self.mult.multiply(int(s))
+ if self.add is None:
+ res = self.mult._add(R, S) # noqa: This is OK.
+ else:
+ res = self.add(self.params.curve.prime, R, S, **self.params.curve.parameters)[0]
+ return action.exit(res)
@public
class MultiplicativeSplitting(ScalarMultiplierCountermeasure):
+ r"""
+ Multiplicative splitting countermeasure.
+
+ Splits the scalar into two parts multiplicatively, multiplies the point with them and adds the results.
+
+ .. math::
+ :class: frame
+
+ &r \xleftarrow{\$} \{0, 1, \ldots, 2^{\text{rand_bits}}\} \\
+ &S = [r]G \\
+ &\textbf{return}\ [k r^{-1} \mod n]S
+
+ """
rand_bits: int
def __init__(self, mult: ScalarMultiplier, rand_bits: int = 32):
+ """
+ :param mult: The multiplier to use.
+ :param rand_bits: How many random bits to sample.
+ """
super().__init__(mult)
self.rand_bits = rand_bits
def multiply(self, scalar: int) -> Point:
if self.params is None or self.point is None:
raise ValueError("Not initialized.")
- r = Mod.random(1 << self.rand_bits)
- R = self.mult.multiply(int(r))
+ with ScalarMultiplicationAction(self.point, self.params, scalar) as action:
+ r = Mod.random(1 << self.rand_bits)
+ R = self.mult.multiply(int(r))
- self.mult.init(self.params, R)
- kr_inv = scalar * mod(int(r), self.params.order).inverse()
- return self.mult.multiply(int(kr_inv))
+ self.mult.init(self.params, R)
+ kr_inv = scalar * mod(int(r), self.params.order).inverse()
+ return action.exit(self.mult.multiply(int(kr_inv)))
@public
class EuclideanSplitting(ScalarMultiplierCountermeasure):
+ r"""
+ Euclidean splitting countermeasure.
+
+ Picks a random value half the size of the curve, then splits the scalar
+ into the remainder and the quotient of the division by the random value.
+
+ .. math::
+ :class: frame
+
+ &r \xleftarrow{\$} \{0, 1, \ldots, 2^{\log_2{(n)}/2}\} \\
+ &S = [r]G \\
+ &k_1 = k \mod r \\
+ &k_2 = \lfloor \frac{k}{r} \rfloor \\
+ &\textbf{return}\ [k_1]G + [k_2]S
+
+ """
add: Optional[AdditionFormula]
def __init__(self, mult: ScalarMultiplier, add: Optional[AdditionFormula] = None):
+ """
+ :param mult: The multiplier to use.
+ :param add: Addition formula to use, if None, the formula from the multiplier is used.
+ """
super().__init__(mult)
self.add = add
def multiply(self, scalar: int) -> Point:
if self.params is None or self.point is None:
raise ValueError("Not initialized.")
+ with ScalarMultiplicationAction(self.point, self.params, scalar) as action:
+ order = self.params.order
+ half_bits = order.bit_length() // 2
+ r = Mod.random(1 << half_bits)
+ R = self.mult.multiply(int(r))
- order = self.params.order
- half_bits = order.bit_length() // 2
- r = Mod.random(1 << half_bits)
- R = self.mult.multiply(int(r))
+ k1 = scalar % int(r)
+ k2 = scalar // int(r)
+ T = self.mult.multiply(k1)
- k1 = scalar % int(r)
- k2 = scalar // int(r)
- T = self.mult.multiply(k1)
+ self.mult.init(self.params, R)
+ S = self.mult.multiply(k2)
- self.mult.init(self.params, R)
- S = self.mult.multiply(k2)
- if self.add is None:
- return self.mult._add(S, T) # noqa: This is OK.
- else:
- return self.add(
- self.params.curve.prime, S, T, **self.params.curve.parameters
- )[0]
+ if self.add is None:
+ res = self.mult._add(S, T) # noqa: This is OK.
+ else:
+ res = self.add(self.params.curve.prime, S, T, **self.params.curve.parameters)[0]
+ return action.exit(res)
diff --git a/pyecsca/ec/mult/base.py b/pyecsca/ec/mult/base.py
index 1c51271..f7a8c5b 100644
--- a/pyecsca/ec/mult/base.py
+++ b/pyecsca/ec/mult/base.py
@@ -7,7 +7,7 @@ from enum import Enum
from public import public
from typing import Mapping, Tuple, Optional, ClassVar, Set, Type
-from pyecsca.ec.context import ResultAction, Action
+from pyecsca.ec.context import ResultAction
from pyecsca.ec.formula import Formula
from pyecsca.ec.params import DomainParameters
from pyecsca.ec.point import Point
diff --git a/pyecsca/sca/re/rpa.py b/pyecsca/sca/re/rpa.py
index e7796cf..4f30d07 100644
--- a/pyecsca/sca/re/rpa.py
+++ b/pyecsca/sca/re/rpa.py
@@ -54,7 +54,7 @@ class MultipleContext(Context):
"""The mapping of points to the formula types they are a result of."""
precomp: MutableMapping[int, Point]
"""The mapping of precomputed multiples to the points they represent."""
- inside: bool
+ inside: List[Action]
"""Whether we are inside a scalarmult/precomp action."""
keep_base: bool
"""Whether to keep the base point when building upon it."""
@@ -65,12 +65,12 @@ class MultipleContext(Context):
self.parents = {}
self.formulas = {}
self.precomp = {}
- self.inside = False
+ self.inside = []
self.keep_base = keep_base
def enter_action(self, action: Action) -> None:
if isinstance(action, (ScalarMultiplicationAction, PrecomputationAction)):
- self.inside = True
+ self.inside.append(action)
if self.base:
# If we already did some computation with this context try to see if we are building on top of it.
if self.base != action.point:
@@ -97,9 +97,9 @@ class MultipleContext(Context):
def exit_action(self, action: Action) -> None:
if isinstance(action, (ScalarMultiplicationAction, PrecomputationAction)):
- self.inside = False
+ self.inside.remove(action)
if isinstance(action, PrecomputationAction):
- self.precomp = action.result
+ self.precomp.update(action.result)
if isinstance(action, FormulaAction) and self.inside:
action = cast(FormulaAction, action)
if isinstance(action.formula, DoublingFormula):
diff --git a/test/sca/test_rpa.py b/test/sca/test_rpa.py
index 7027a7d..af90fb4 100644
--- a/test/sca/test_rpa.py
+++ b/test/sca/test_rpa.py
@@ -4,6 +4,7 @@ from math import isqrt
import pytest
from pyecsca.ec.context import local
+from pyecsca.ec.countermeasures import AdditiveSplitting
from pyecsca.ec.curve import EllipticCurve
from pyecsca.ec.mod import mod
from pyecsca.ec.model import ShortWeierstrassModel
@@ -128,6 +129,11 @@ def test_multiples_kind(rpa_params):
assert multiples_precomp != multiples_necessary
+def test_multiples_additive(rpa_params):
+ mults = multiples_computed(1454656138887897564, rpa_params, LTRMultiplier, lambda *args, **kwargs: AdditiveSplitting(LTRMultiplier(*args, **kwargs)), True, True, kind="precomp+necessary")
+ assert mults is not None
+
+
def test_x0_point(rpa_params):
res = rpa_point_x0(rpa_params)
assert res is not None