aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pyecsca/ec/countermeasures.py33
-rw-r--r--test/ec/test_countermeasures.py54
2 files changed, 69 insertions, 18 deletions
diff --git a/pyecsca/ec/countermeasures.py b/pyecsca/ec/countermeasures.py
index a310ce4..86f7177 100644
--- a/pyecsca/ec/countermeasures.py
+++ b/pyecsca/ec/countermeasures.py
@@ -1,7 +1,7 @@
"""Provides several countermeasures against side-channel attacks."""
from abc import ABC, abstractmethod
-from typing import Optional
+from typing import Optional, Callable
from public import public
@@ -30,8 +30,13 @@ class ScalarMultiplierCountermeasure(ABC):
bits: Optional[int]
"""The bit-length to use, if any."""
- def __init__(self, mult: "ScalarMultiplier | ScalarMultiplierCountermeasure"):
+ def __init__(
+ self,
+ mult: "ScalarMultiplier | ScalarMultiplierCountermeasure",
+ rng: Callable[[int], Mod] = Mod.random,
+ ):
self.mult = mult
+ self.rng = rng
def init(self, params: DomainParameters, point: Point, bits: Optional[int] = None):
"""Initialize the countermeasure with the parameters and the point."""
@@ -76,13 +81,14 @@ class GroupScalarRandomization(ScalarMultiplierCountermeasure):
def __init__(
self,
mult: "ScalarMultiplier | ScalarMultiplierCountermeasure",
+ rng: Callable[[int], Mod] = Mod.random,
rand_bits: int = 32,
):
"""
:param mult: The multiplier to use.
:param rand_bits: How many random bits to sample.
"""
- super().__init__(mult)
+ super().__init__(mult, rng)
self.rand_bits = rand_bits
def multiply(self, scalar: int) -> Point:
@@ -90,7 +96,7 @@ class GroupScalarRandomization(ScalarMultiplierCountermeasure):
raise ValueError("Not initialized.")
with ScalarMultiplicationAction(self.point, self.params, scalar) as action:
order = self.params.order
- mask = int(Mod.random(1 << self.rand_bits))
+ mask = int(self.rng(1 << self.rand_bits))
masked_scalar = scalar + mask * order
bits = max(self.bits, self.rand_bits + order.bit_length()) + 1
self.mult.init(
@@ -121,13 +127,14 @@ class AdditiveSplitting(ScalarMultiplierCountermeasure):
def __init__(
self,
mult: "ScalarMultiplier | ScalarMultiplierCountermeasure",
+ rng: Callable[[int], Mod] = Mod.random,
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)
+ super().__init__(mult, rng)
self.add = add
def _add(self, R: Point, S: Point) -> Point: # noqa
@@ -146,7 +153,7 @@ class AdditiveSplitting(ScalarMultiplierCountermeasure):
raise ValueError("Not initialized.")
with ScalarMultiplicationAction(self.point, self.params, scalar) as action:
order = self.params.order
- r = Mod.random(order)
+ r = self.rng(order)
s = scalar - r
bits = max(self.bits, order.bit_length())
self.mult.init(self.params, self.point, bits)
@@ -177,20 +184,21 @@ class MultiplicativeSplitting(ScalarMultiplierCountermeasure):
def __init__(
self,
mult: "ScalarMultiplier | ScalarMultiplierCountermeasure",
+ rng: Callable[[int], Mod] = Mod.random,
rand_bits: int = 32,
):
"""
:param mult: The multiplier to use.
:param rand_bits: How many random bits to sample.
"""
- super().__init__(mult)
+ super().__init__(mult, rng)
self.rand_bits = rand_bits
def multiply(self, scalar: int) -> Point:
if self.params is None or self.point is None or self.bits is None:
raise ValueError("Not initialized.")
with ScalarMultiplicationAction(self.point, self.params, scalar) as action:
- r = Mod.random(1 << self.rand_bits)
+ r = self.rng(1 << self.rand_bits)
self.mult.init(self.params, self.point, self.rand_bits)
R = self.mult.multiply(int(r))
@@ -225,13 +233,14 @@ class EuclideanSplitting(ScalarMultiplierCountermeasure):
def __init__(
self,
mult: "ScalarMultiplier | ScalarMultiplierCountermeasure",
+ rng: Callable[[int], Mod] = Mod.random,
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)
+ super().__init__(mult, rng)
self.add = add
def _add(self, R: Point, S: Point) -> Point: # noqa
@@ -250,7 +259,7 @@ class EuclideanSplitting(ScalarMultiplierCountermeasure):
raise ValueError("Not initialized.")
with ScalarMultiplicationAction(self.point, self.params, scalar) as action:
half_bits = self.bits // 2
- r = Mod.random(1 << half_bits)
+ r = self.rng(1 << half_bits)
self.mult.init(self.params, self.point, half_bits)
R = self.mult.multiply(int(r)) # r bounded by half_bits
@@ -259,7 +268,9 @@ class EuclideanSplitting(ScalarMultiplierCountermeasure):
T = self.mult.multiply(k1) # k1 bounded by half_bits
self.mult.init(self.params, R, self.bits)
- S = self.mult.multiply(k2) # k2 (in worst case) bounded by bits, but in practice closer to half_bits
+ S = self.mult.multiply(
+ k2
+ ) # k2 (in worst case) bounded by bits, but in practice closer to half_bits
res = self._add(S, T)
return action.exit(res)
diff --git a/test/ec/test_countermeasures.py b/test/ec/test_countermeasures.py
index 5d059f9..8365be0 100644
--- a/test/ec/test_countermeasures.py
+++ b/test/ec/test_countermeasures.py
@@ -10,6 +10,7 @@ from pyecsca.ec.countermeasures import (
EuclideanSplitting,
BrumleyTuveri,
)
+from pyecsca.ec.mod import mod
from pyecsca.ec.mult import *
@@ -245,12 +246,15 @@ def test_brumley_tuveri(mults, secp128r1, num):
assert raw.equals(masked)
-@pytest.mark.parametrize("scalar", [
- 3253857902090173296443513219124437746,
- 1234567893141592653589793238464338327,
- 86728612699079982903603364383639280149,
- 60032993417060801067503559426926851620
-])
+@pytest.mark.parametrize(
+ "scalar",
+ [
+ 3253857902090173296443513219124437746,
+ 1234567893141592653589793238464338327,
+ 86728612699079982903603364383639280149,
+ 60032993417060801067503559426926851620,
+ ],
+)
@pytest.mark.parametrize(
"one,two",
product(
@@ -269,7 +273,7 @@ def test_combination(scalar, one, two, secp128r1):
pytest.skip("Skip identical combinations.")
mult = LTRMultiplier(
secp128r1.curve.coordinate_model.formulas["add-2015-rcb"],
- secp128r1.curve.coordinate_model.formulas["dbl-2015-rcb"]
+ secp128r1.curve.coordinate_model.formulas["dbl-2015-rcb"],
)
mult.init(secp128r1, secp128r1.generator)
raw = mult.multiply(scalar)
@@ -288,3 +292,39 @@ def test_combination(scalar, one, two, secp128r1):
combo.init(secp128r1, secp128r1.generator)
masked = combo.multiply(scalar)
assert raw.equals(masked)
+
+
+@pytest.mark.parametrize(
+ "scalar",
+ [
+ 3253857902090173296443513219124437746,
+ 1234567893141592653589793238464338327,
+ 86728612699079982903603364383639280149,
+ 60032993417060801067503559426926851620,
+ ],
+)
+@pytest.mark.parametrize(
+ "ctr",
+ (
+ GroupScalarRandomization,
+ AdditiveSplitting,
+ EuclideanSplitting,
+ MultiplicativeSplitting,
+ BrumleyTuveri,
+ ),
+)
+def test_rng(scalar, ctr, secp128r1):
+ mult = LTRMultiplier(
+ secp128r1.curve.coordinate_model.formulas["add-2015-rcb"],
+ secp128r1.curve.coordinate_model.formulas["dbl-2015-rcb"],
+ )
+ mult.init(secp128r1, secp128r1.generator)
+ raw = mult.multiply(scalar)
+
+ def rng(n):
+ return mod(123456789, n)
+
+ m = ctr(mult, rng)
+ m.init(secp128r1, secp128r1.generator)
+ masked = m.multiply(scalar)
+ assert raw.equals(masked)