aboutsummaryrefslogtreecommitdiffhomepage
path: root/pyecsca/ec
diff options
context:
space:
mode:
authorJ08nY2025-03-12 20:01:29 +0100
committerJ08nY2025-03-12 20:01:29 +0100
commit4440cf1e5fd28456d8b259d498f9831d46acef73 (patch)
treed232b04b6d588477e1401fbd8d7ae769066d8ad5 /pyecsca/ec
parent345d5883e5e75ed02282107127a8e65b67435247 (diff)
downloadpyecsca-4440cf1e5fd28456d8b259d498f9831d46acef73.tar.gz
pyecsca-4440cf1e5fd28456d8b259d498f9831d46acef73.tar.zst
pyecsca-4440cf1e5fd28456d8b259d498f9831d46acef73.zip
Docs for countermeasures.
Diffstat (limited to 'pyecsca/ec')
-rw-r--r--pyecsca/ec/countermeasures.py152
-rw-r--r--pyecsca/ec/mult/base.py2
2 files changed, 116 insertions, 38 deletions
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