diff options
| -rw-r--r-- | pyecsca/ec/countermeasures.py | 100 | ||||
| -rw-r--r-- | test/ec/test_countermeasures.py | 30 | ||||
| -rw-r--r-- | test/sca/test_rpa.py | 2 |
3 files changed, 112 insertions, 20 deletions
diff --git a/pyecsca/ec/countermeasures.py b/pyecsca/ec/countermeasures.py index 853260c..5e940e2 100644 --- a/pyecsca/ec/countermeasures.py +++ b/pyecsca/ec/countermeasures.py @@ -5,7 +5,7 @@ from typing import Optional, Callable, get_type_hints, ClassVar from public import public -from pyecsca.ec.formula import AdditionFormula +from pyecsca.ec.formula import AdditionFormula, NegationFormula from pyecsca.ec.mod import Mod, mod from pyecsca.ec.mult import ScalarMultiplier, ScalarMultiplicationAction from pyecsca.ec.params import DomainParameters @@ -84,6 +84,26 @@ class ScalarMultiplierCountermeasure(ABC): mults = [mult] * num return cls(*mults, **kwargs) + def _apply_formula(self, shortname: str, *points: Point) -> Point: + if formula := getattr(self, shortname, None): + return formula( + self.params.curve.prime, + *points, + **self.params.curve.parameters, # type: ignore + )[0] + else: + for mult in self.mults: + if mult_formula := getattr(mult, f"_{shortname}", None): + return mult_formula(*points) # type: ignore + else: + raise ValueError(f"No formula '{shortname}' available.") + + def _add(self, R: Point, S: Point) -> Point: # noqa + return self._apply_formula("add", R, S) + + def _neg(self, P: Point) -> Point: + return self._apply_formula("neg", P) + @public class GroupScalarRandomization(ScalarMultiplierCountermeasure): @@ -99,6 +119,7 @@ class GroupScalarRandomization(ScalarMultiplierCountermeasure): &\textbf{return}\ [k + r n]G """ + nmults = 1 rand_bits: int @@ -110,6 +131,7 @@ class GroupScalarRandomization(ScalarMultiplierCountermeasure): ): """ :param mult: The multiplier to use. + :param rng: The random number generator to use. :param rand_bits: How many random bits to sample. """ super().__init__(mult, rng=rng) @@ -145,6 +167,7 @@ class AdditiveSplitting(ScalarMultiplierCountermeasure): &\textbf{return}\ [k - r]G + [r]G """ + nmults = 2 add: Optional[AdditionFormula] @@ -156,26 +179,14 @@ class AdditiveSplitting(ScalarMultiplierCountermeasure): add: Optional[AdditionFormula] = None, ): """ - :param mult: The multiplier to use. + :param mult1: The multiplier to use. + :param mult2: The multiplier to use. + :param rng: The random number generator to use. :param add: Addition formula to use, if None, the formula from the multiplier is used. """ super().__init__(mult1, mult2, rng=rng) self.add = add - def _add(self, R: Point, S: Point) -> Point: # noqa - if self.add is None: - for mult in self.mults: - try: - return mult._add(R, S) # type: ignore - except AttributeError: - pass - else: - raise ValueError("No addition formula available.") - else: - return self.add( - self.params.curve.prime, R, S, **self.params.curve.parameters # type: ignore - )[0] - 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.") @@ -211,6 +222,7 @@ class MultiplicativeSplitting(ScalarMultiplierCountermeasure): &\textbf{return}\ [k r^{-1} \mod n]S """ + nmults = 2 rand_bits: int @@ -222,7 +234,9 @@ class MultiplicativeSplitting(ScalarMultiplierCountermeasure): rand_bits: int = 32, ): """ - :param mult: The multiplier to use. + :param mult1: The multiplier to use. + :param mult2: The multiplier to use. + :param rng: The random number generator to use. :param rand_bits: How many random bits to sample. """ super().__init__(mult1, mult2, rng=rng) @@ -261,6 +275,7 @@ class EuclideanSplitting(ScalarMultiplierCountermeasure): &\textbf{return}\ [k_1]G + [k_2]S """ + nmults = 3 add: Optional[AdditionFormula] @@ -273,7 +288,10 @@ class EuclideanSplitting(ScalarMultiplierCountermeasure): add: Optional[AdditionFormula] = None, ): """ - :param mult: The multiplier to use. + :param mult1: The multiplier to use. + :param mult2: The multiplier to use. + :param mult3: The multiplier to use. + :param rng: The random number generator to use. :param add: Addition formula to use, if None, the formula from the multiplier is used. """ super().__init__(mult1, mult2, mult3, rng=rng) @@ -335,6 +353,7 @@ class BrumleyTuveri(ScalarMultiplierCountermeasure): &\textbf{return}\ [\hat{k}]G """ + nmults = 1 def __init__( @@ -344,6 +363,7 @@ class BrumleyTuveri(ScalarMultiplierCountermeasure): ): """ :param mult: The multiplier to use. + :param rng: The random number generator to use. """ super().__init__(mult, rng=rng) @@ -361,3 +381,47 @@ class BrumleyTuveri(ScalarMultiplierCountermeasure): if scalar.bit_length() <= n.bit_length(): scalar += n return action.exit(self.mults[0].multiply(scalar)) + + +class PointBlinding(ScalarMultiplierCountermeasure): + """Point blinding countermeasure.""" + + nmults = 2 + add: Optional[AdditionFormula] + neg: Optional[NegationFormula] + + def __init__( + self, + mult1: "ScalarMultiplier | ScalarMultiplierCountermeasure", + mult2: "ScalarMultiplier | ScalarMultiplierCountermeasure", + rng: Callable[[int], Mod] = Mod.random, + add: Optional[AdditionFormula] = None, + neg: Optional[NegationFormula] = None, + ): + """ + + :param mult1: The multiplier to use. + :param mult2: The multiplier to use. + :param rng: The random number generator to use. + :param add: Addition formula to use, if None, the formula from the multiplier is used. + :param neg: Negation formula to use, if None, the formula from the multiplier is used. + """ + super().__init__(mult1, mult2, rng=rng) + self.add = add + self.neg = neg + + 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 = self.params.curve.affine_random().to_model( + self.params.curve.coordinate_model, self.params.curve + ) + self.mults[0].init(self.params, R, self.bits) + S = self.mults[0].multiply(int(scalar)) + + T = self._add(self.point, R) + self.mults[1].init(self.params, T, self.bits) + Q = self.mults[1].multiply(int(scalar)) + + return action.exit(self._add(Q, self._neg(S))) diff --git a/test/ec/test_countermeasures.py b/test/ec/test_countermeasures.py index 946261c..c3a6ee3 100644 --- a/test/ec/test_countermeasures.py +++ b/test/ec/test_countermeasures.py @@ -9,6 +9,7 @@ from pyecsca.ec.countermeasures import ( MultiplicativeSplitting, EuclideanSplitting, BrumleyTuveri, + PointBlinding, ) from pyecsca.ec.mod import mod from pyecsca.ec.mult import * @@ -247,6 +248,27 @@ def test_brumley_tuveri(mults, secp128r1, num): @pytest.mark.parametrize( + "num", + [ + 3253857902090173296443513219124437746, + 1234567893141592653589793238464338327, + ], +) +def test_point_blinding(mults, secp128r1, num): + mult = copy(mults[0]) + mult.init(secp128r1, secp128r1.generator) + raw = mult.multiply(num) + + neg = secp128r1.curve.coordinate_model.formulas["neg"] + + for mult in mults: + pb = PointBlinding(mult, mult, neg=neg) + pb.init(secp128r1, secp128r1.generator) + masked = pb.multiply(num) + assert raw.equals(masked) + + +@pytest.mark.parametrize( "scalar", [ 3253857902090173296443513219124437746, @@ -264,6 +286,7 @@ def test_brumley_tuveri(mults, secp128r1, num): MultiplicativeSplitting, EuclideanSplitting, BrumleyTuveri, + PointBlinding, ), repeat=2, ), @@ -279,15 +302,20 @@ def test_combination(scalar, one, two, secp128r1): mult.init(secp128r1, secp128r1.generator) raw = mult.multiply(scalar) - add = mult.formulas["add"] + add = secp128r1.curve.coordinate_model.formulas["add-2015-rcb"] + neg = secp128r1.curve.coordinate_model.formulas["neg"] if one in (AdditiveSplitting, EuclideanSplitting): layer_one = one.from_single(mult, add=add) + elif one == PointBlinding: + layer_one = one.from_single(mult, neg=neg) else: layer_one = one.from_single(mult) if two in (AdditiveSplitting, EuclideanSplitting): kws = {"add": add} + elif two == PointBlinding: + kws = {"neg": neg} else: kws = {} diff --git a/test/sca/test_rpa.py b/test/sca/test_rpa.py index 01293af..e92ec44 100644 --- a/test/sca/test_rpa.py +++ b/test/sca/test_rpa.py @@ -153,7 +153,7 @@ def test_multiples_kind(rpa_params): 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") + mults = multiples_computed(1454656138887897564, rpa_params, LTRMultiplier, lambda *args, **kwargs: AdditiveSplitting.from_single(LTRMultiplier(*args, **kwargs)), True, True, kind="precomp+necessary") assert mults is not None |
