From 2ad8605e48ac95e656e32e5cfcbb2c2847a8b0ff Mon Sep 17 00:00:00 2001 From: J08nY Date: Fri, 3 Oct 2025 12:43:02 +0200 Subject: Allow for combinations of countermeasures. --- test/ec/test_countermeasures.py | 77 +++++++++++++++++++++++++++++++++-------- 1 file changed, 63 insertions(+), 14 deletions(-) (limited to 'test') diff --git a/test/ec/test_countermeasures.py b/test/ec/test_countermeasures.py index 1bf1477..5d059f9 100644 --- a/test/ec/test_countermeasures.py +++ b/test/ec/test_countermeasures.py @@ -7,7 +7,8 @@ from pyecsca.ec.countermeasures import ( GroupScalarRandomization, AdditiveSplitting, MultiplicativeSplitting, - EuclideanSplitting, BrumleyTuveri, + EuclideanSplitting, + BrumleyTuveri, ) from pyecsca.ec.mult import * @@ -122,7 +123,10 @@ def mults(secp128r1, add, dbl): ) for combination in product(*bgmw_options.values()) ] - comb_options = {"width": (2, 3, 4, 5), "accumulation_order": tuple(AccumulationOrder)} + comb_options = { + "width": (2, 3, 4, 5), + "accumulation_order": tuple(AccumulationOrder), + } combs = [ CombMultiplier( add, dbl, scl=scale, **dict(zip(comb_options.keys(), combination)) @@ -131,18 +135,18 @@ def mults(secp128r1, add, dbl): ] return ( - ltrs - + rtls - + bnafs - + wnafs - + booths - + [CoronMultiplier(add, dbl, scale)] - + ladders - + fixeds - + slides - + precomps - + bgmws - + combs + ltrs + + rtls + + bnafs + + wnafs + + booths + + [CoronMultiplier(add, dbl, scale)] + + ladders + + fixeds + + slides + + precomps + + bgmws + + combs ) @@ -239,3 +243,48 @@ def test_brumley_tuveri(mults, secp128r1, num): bt.init(secp128r1, secp128r1.generator) masked = bt.multiply(num) assert raw.equals(masked) + + +@pytest.mark.parametrize("scalar", [ + 3253857902090173296443513219124437746, + 1234567893141592653589793238464338327, + 86728612699079982903603364383639280149, + 60032993417060801067503559426926851620 +]) +@pytest.mark.parametrize( + "one,two", + product( + ( + GroupScalarRandomization, + AdditiveSplitting, + MultiplicativeSplitting, + EuclideanSplitting, + BrumleyTuveri, + ), + repeat=2, + ), +) +def test_combination(scalar, one, two, secp128r1): + if one == two: + pytest.skip("Skip identical combinations.") + 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) + + add = mult.formulas["add"] + + if one in (AdditiveSplitting, EuclideanSplitting): + layer_one = one(mult, add) + else: + layer_one = one(mult) + + if two in (AdditiveSplitting, EuclideanSplitting): + combo = two(layer_one, add) + else: + combo = two(layer_one) + combo.init(secp128r1, secp128r1.generator) + masked = combo.multiply(scalar) + assert raw.equals(masked) -- cgit v1.2.3-70-g09d2 From 2f869828b04fd1d7601d93799a2361ec81e0f4e2 Mon Sep 17 00:00:00 2001 From: J08nY Date: Fri, 3 Oct 2025 13:05:10 +0200 Subject: Add a way to control randomness in coountermeasures. --- pyecsca/ec/countermeasures.py | 33 ++++++++++++++++--------- test/ec/test_countermeasures.py | 54 +++++++++++++++++++++++++++++++++++------ 2 files changed, 69 insertions(+), 18 deletions(-) (limited to 'test') 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) -- cgit v1.2.3-70-g09d2 From 4382db8c501b9b9a39ff67d33ce79bd16dbf5747 Mon Sep 17 00:00:00 2001 From: J08nY Date: Fri, 3 Oct 2025 13:29:17 +0200 Subject: Fix add arg to ctrs. --- test/ec/test_countermeasures.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/test/ec/test_countermeasures.py b/test/ec/test_countermeasures.py index 8365be0..62db0d4 100644 --- a/test/ec/test_countermeasures.py +++ b/test/ec/test_countermeasures.py @@ -281,12 +281,12 @@ def test_combination(scalar, one, two, secp128r1): add = mult.formulas["add"] if one in (AdditiveSplitting, EuclideanSplitting): - layer_one = one(mult, add) + layer_one = one(mult, add=add) else: layer_one = one(mult) if two in (AdditiveSplitting, EuclideanSplitting): - combo = two(layer_one, add) + combo = two(layer_one, add=add) else: combo = two(layer_one) combo.init(secp128r1, secp128r1.generator) -- cgit v1.2.3-70-g09d2