diff options
| -rw-r--r-- | pyecsca/ec/countermeasures.py | 33 | ||||
| -rw-r--r-- | test/ec/test_countermeasures.py | 54 |
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) |
