diff options
| -rw-r--r-- | pyecsca/ec/countermeasures.py | 8 | ||||
| -rw-r--r-- | pyecsca/ec/mult/base.py | 6 | ||||
| -rw-r--r-- | pyecsca/ec/mult/binary.py | 4 | ||||
| -rw-r--r-- | pyecsca/ec/mult/comb.py | 14 | ||||
| -rw-r--r-- | pyecsca/ec/mult/fake.py | 2 | ||||
| -rw-r--r-- | pyecsca/ec/mult/fixed.py | 12 | ||||
| -rw-r--r-- | pyecsca/ec/mult/ladder.py | 7 | ||||
| -rw-r--r-- | pyecsca/ec/mult/naf.py | 8 | ||||
| -rw-r--r-- | pyecsca/ec/mult/window.py | 14 | ||||
| -rw-r--r-- | test/ec/test_countermeasures.py | 199 | ||||
| -rw-r--r-- | test/ec/test_mult.py | 6 |
11 files changed, 205 insertions, 75 deletions
diff --git a/pyecsca/ec/countermeasures.py b/pyecsca/ec/countermeasures.py index 8a479b6..19afb06 100644 --- a/pyecsca/ec/countermeasures.py +++ b/pyecsca/ec/countermeasures.py @@ -70,6 +70,11 @@ class GroupScalarRandomization(ScalarMultiplierCountermeasure): super().__init__(mult) self.rand_bits = rand_bits + def init(self, params: DomainParameters, point: Point): + self.params = params + self.point = point + self.mult.init(self.params, self.point, bits=params.full_order.bit_length() + self.rand_bits) + def multiply(self, scalar: int) -> Point: if self.params is None or self.point is None: raise ValueError("Not initialized.") @@ -189,8 +194,7 @@ class EuclideanSplitting(ScalarMultiplierCountermeasure): 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 + half_bits = self.params.order.bit_length() // 2 r = Mod.random(1 << half_bits) R = self.mult.multiply(int(r)) diff --git a/pyecsca/ec/mult/base.py b/pyecsca/ec/mult/base.py index f7a8c5b..15f2fd5 100644 --- a/pyecsca/ec/mult/base.py +++ b/pyecsca/ec/mult/base.py @@ -87,6 +87,7 @@ class ScalarMultiplier(ABC): """All formulas the multiplier was initialized with.""" _params: DomainParameters _point: Point + _bits: int _initialized: bool = False def __init__(self, short_circuit: bool = True, **formulas: Optional[Formula]): @@ -202,7 +203,7 @@ class ScalarMultiplier(ABC): def __repr__(self): return f"{self.__class__.__name__}({', '.join(map(str, self.formulas.values()))}, short_circuit={self.short_circuit})" - def init(self, params: DomainParameters, point: Point): + def init(self, params: DomainParameters, point: Point, bits: Optional[int] = None): """ Initialize the scalar multiplier with :paramref:`~.init.params` and a :paramref:`~.init.point`. @@ -211,6 +212,8 @@ class ScalarMultiplier(ABC): :param params: The domain parameters to initialize the multiplier with. :param point: The point to initialize the multiplier with. + :param bits: The number of bits to use in the scalar multiplication (i.e. no scalar will be larger than 2^bits). + The default is the bit length of the full order of the curve (including cofactor). """ coord_model = set(self.formulas.values()).pop().coordinate_model if ( @@ -222,6 +225,7 @@ class ScalarMultiplier(ABC): ) self._params = params self._point = point + self._bits = bits if bits is not None else params.full_order.bit_length() self._initialized = True @abstractmethod diff --git a/pyecsca/ec/mult/binary.py b/pyecsca/ec/mult/binary.py index 5b6bed7..f0cb5ac 100644 --- a/pyecsca/ec/mult/binary.py +++ b/pyecsca/ec/mult/binary.py @@ -94,7 +94,7 @@ class DoubleAndAddMultiplier(AccumulatorMultiplier, ScalarMultiplier, ABC): if self.complete: q = self._point r = copy(self._params.curve.neutral) - top = self._params.order.bit_length() - 1 + top = self._bits - 1 else: q = copy(self._point) r = copy(self._point) @@ -112,7 +112,7 @@ class DoubleAndAddMultiplier(AccumulatorMultiplier, ScalarMultiplier, ABC): q = self._point r = copy(self._params.curve.neutral) if self.complete: - top = self._params.order.bit_length() + top = self._bits else: top = scalar.bit_length() for _ in range(top): diff --git a/pyecsca/ec/mult/comb.py b/pyecsca/ec/mult/comb.py index 35d1678..1a6c0c2 100644 --- a/pyecsca/ec/mult/comb.py +++ b/pyecsca/ec/mult/comb.py @@ -85,10 +85,10 @@ class BGMWMultiplier(AccumulatorMultiplier, PrecompMultiplier, ScalarMultiplier) def __repr__(self): return f"{self.__class__.__name__}({', '.join(map(str, self.formulas.values()))}, short_circuit={self.short_circuit}, width={self.width}, direction={self.direction.name}, accumulation_order={self.accumulation_order.name})" - def init(self, params: DomainParameters, point: Point): + def init(self, params: DomainParameters, point: Point, bits: Optional[int] = None): with PrecomputationAction(params, point) as action: - super().init(params, point) - d = ceil(params.order.bit_length() / self.width) + super().init(params, point, bits) + d = ceil(self._bits / self.width) self._points = {} current_point = point for i in range(d): @@ -179,10 +179,10 @@ class CombMultiplier(AccumulatorMultiplier, PrecompMultiplier, ScalarMultiplier) def __repr__(self): return f"{self.__class__.__name__}({', '.join(map(str, self.formulas.values()))}, short_circuit={self.short_circuit}, width={self.width}, accumulation_order={self.accumulation_order.name})" - def init(self, params: DomainParameters, point: Point): + def init(self, params: DomainParameters, point: Point, bits: Optional[int] = None): with PrecomputationAction(params, point) as action: - super().init(params, point) - d = ceil(params.order.bit_length() / self.width) + super().init(params, point, bits) + d = ceil(self._bits / self.width) base_points = {} current_point = point for i in range(self.width): @@ -208,7 +208,7 @@ class CombMultiplier(AccumulatorMultiplier, PrecompMultiplier, ScalarMultiplier) if scalar == 0: return action.exit(copy(self._params.curve.neutral)) q = copy(self._params.curve.neutral) - d = ceil(self._params.order.bit_length() / self.width) + d = ceil(self._bits / self.width) recoded = convert_base(scalar, 2**d) if len(recoded) != self.width: recoded.extend([0] * (self.width - len(recoded))) diff --git a/pyecsca/ec/mult/fake.py b/pyecsca/ec/mult/fake.py index f89d622..391696b 100644 --- a/pyecsca/ec/mult/fake.py +++ b/pyecsca/ec/mult/fake.py @@ -9,7 +9,7 @@ from pyecsca.ec.params import DomainParameters def fake_mult(mult_class: Type[ScalarMultiplier], mult_factory: Callable, params: DomainParameters) -> ScalarMultiplier: """ - Get a multiplier with FakeFormulas. + Get a multiplier with `FakeFormula`s. :param mult_class: The class of the scalar multiplier to use. :param mult_factory: A callable that takes the formulas and instantiates the multiplier. diff --git a/pyecsca/ec/mult/fixed.py b/pyecsca/ec/mult/fixed.py index 2c01648..070aaec 100644 --- a/pyecsca/ec/mult/fixed.py +++ b/pyecsca/ec/mult/fixed.py @@ -90,20 +90,20 @@ class FullPrecompMultiplier(AccumulatorMultiplier, PrecompMultiplier, ScalarMult def __repr__(self): return f"{self.__class__.__name__}({', '.join(map(str, self.formulas.values()))}, short_circuit={self.short_circuit}, accumulation_order={self.accumulation_order.name}, always={self.always}, complete={self.complete})" - def init(self, params: DomainParameters, point: Point): + def init(self, params: DomainParameters, point: Point, bits: Optional[int] = None): with PrecomputationAction(params, point) as action: - super().init(params, point) + super().init(params, point, bits) self._points = {} current_point = point - for i in range(params.order.bit_length() + 1): + for i in range(self._bits + 1): self._points[i] = current_point - if i != params.order.bit_length(): + if i != self._bits: current_point = self._dbl(current_point) action.exit(self._points) def _ltr(self, scalar: int) -> Point: if self.complete: - top = self._params.order.bit_length() - 1 + top = self._bits - 1 else: top = scalar.bit_length() - 1 r = copy(self._params.curve.neutral) @@ -118,7 +118,7 @@ class FullPrecompMultiplier(AccumulatorMultiplier, PrecompMultiplier, ScalarMult def _rtl(self, scalar: int) -> Point: r = copy(self._params.curve.neutral) if self.complete: - top = self._params.order.bit_length() + top = self._bits else: top = scalar.bit_length() for i in range(top): diff --git a/pyecsca/ec/mult/ladder.py b/pyecsca/ec/mult/ladder.py index 5f5d8a2..3635ada 100644 --- a/pyecsca/ec/mult/ladder.py +++ b/pyecsca/ec/mult/ladder.py @@ -12,6 +12,7 @@ from pyecsca.ec.formula import ( LadderFormula, DifferentialAdditionFormula, ) +from pyecsca.ec.params import DomainParameters from pyecsca.ec.point import Point @@ -87,7 +88,7 @@ class LadderMultiplier(ScalarMultiplier): if self.complete: p0 = copy(self._params.curve.neutral) p1 = self._point - top = self._params.full_order.bit_length() - 1 + top = self._bits - 1 elif self.full: p0 = copy(self._params.curve.neutral) p1 = self._point @@ -154,7 +155,7 @@ class SimpleLadderMultiplier(ScalarMultiplier): if scalar == 0: return action.exit(copy(self._params.curve.neutral)) if self.complete: - top = self._params.full_order.bit_length() - 1 + top = self._bits - 1 else: top = scalar.bit_length() - 1 p0 = copy(self._params.curve.neutral) @@ -232,7 +233,7 @@ class DifferentialLadderMultiplier(ScalarMultiplier): if self.complete: p0 = copy(self._params.curve.neutral) p1 = copy(q) - top = self._params.full_order.bit_length() - 1 + top = self._bits - 1 elif self.full: p0 = copy(self._params.curve.neutral) p1 = copy(q) diff --git a/pyecsca/ec/mult/naf.py b/pyecsca/ec/mult/naf.py index 83dc0dc..b886552 100644 --- a/pyecsca/ec/mult/naf.py +++ b/pyecsca/ec/mult/naf.py @@ -81,9 +81,9 @@ class BinaryNAFMultiplier(AccumulatorMultiplier, PrecompMultiplier, ScalarMultip def __repr__(self): return f"{self.__class__.__name__}({', '.join(map(str, self.formulas.values()))}, short_circuit={self.short_circuit}, direction={self.direction.name}, accumulation_order={self.accumulation_order.name})" - def init(self, params: DomainParameters, point: Point): + def init(self, params: DomainParameters, point: Point, bits: Optional[int] = None): with PrecomputationAction(params, point) as action: - super().init(params, point) + super().init(params, point, bits) self._point_neg = self._neg(point) action.exit({-1: self._point_neg}) @@ -195,9 +195,9 @@ class WindowNAFMultiplier(AccumulatorMultiplier, PrecompMultiplier, ScalarMultip def __repr__(self): return f"{self.__class__.__name__}({', '.join(map(str, self.formulas.values()))}, short_circuit={self.short_circuit}, width={self.width}, precompute_negation={self.precompute_negation}, accumulation_order={self.accumulation_order.name})" - def init(self, params: DomainParameters, point: Point): + def init(self, params: DomainParameters, point: Point, bits: Optional[int] = None): with PrecomputationAction(params, point) as action: - super().init(params, point) + super().init(params, point, bits) self._points = {} self._points_neg = {} current_point = point diff --git a/pyecsca/ec/mult/window.py b/pyecsca/ec/mult/window.py index 6fbee24..1a0ecec 100644 --- a/pyecsca/ec/mult/window.py +++ b/pyecsca/ec/mult/window.py @@ -90,9 +90,9 @@ class SlidingWindowMultiplier(AccumulatorMultiplier, PrecompMultiplier, ScalarMu def __repr__(self): return f"{self.__class__.__name__}({', '.join(map(str, self.formulas.values()))}, short_circuit={self.short_circuit}, width={self.width}, recoding_direction={self.recoding_direction.name}, accumulation_order={self.accumulation_order.name})" - def init(self, params: DomainParameters, point: Point): + def init(self, params: DomainParameters, point: Point, bits: Optional[int] = None): with PrecomputationAction(params, point) as action: - super().init(params, point) + super().init(params, point, bits) self._points = {} current_point = point double_point = self._dbl(point) @@ -186,9 +186,9 @@ class FixedWindowLTRMultiplier(AccumulatorMultiplier, PrecompMultiplier, ScalarM def __repr__(self): return f"{self.__class__.__name__}({', '.join(map(str, self.formulas.values()))}, short_circuit={self.short_circuit}, m={self.m}, accumulation_order={self.accumulation_order.name})" - def init(self, params: DomainParameters, point: Point): + def init(self, params: DomainParameters, point: Point, bits: Optional[int] = None): with PrecomputationAction(params, point) as action: - super().init(params, point) + super().init(params, point, bits) double_point = self._dbl(point) self._points = {1: point, 2: double_point} current_point = double_point @@ -298,9 +298,9 @@ class WindowBoothMultiplier(AccumulatorMultiplier, PrecompMultiplier, ScalarMult def __repr__(self): return f"{self.__class__.__name__}({', '.join(map(str, self.formulas.values()))}, short_circuit={self.short_circuit}, width={self.width}, precompute_negation={self.precompute_negation}, accumulation_order={self.accumulation_order.name})" - def init(self, params: DomainParameters, point: Point): + def init(self, params: DomainParameters, point: Point, bits: Optional[int] = None): with PrecomputationAction(params, point) as actions: - super().init(params, point) + super().init(params, point, bits) double_point = self._dbl(point) self._points = {1: point, 2: double_point} if self.precompute_negation: @@ -323,7 +323,7 @@ class WindowBoothMultiplier(AccumulatorMultiplier, PrecompMultiplier, ScalarMult if scalar == 0: return action.exit(copy(self._params.curve.neutral)) scalar_booth = booth_window( - scalar, self.width, self._params.order.bit_length() + scalar, self.width, self._bits ) q = copy(self._params.curve.neutral) for val in scalar_booth: diff --git a/test/ec/test_countermeasures.py b/test/ec/test_countermeasures.py index 0559d26..8a572e8 100644 --- a/test/ec/test_countermeasures.py +++ b/test/ec/test_countermeasures.py @@ -1,3 +1,6 @@ +from itertools import product +from copy import copy + import pytest from pyecsca.ec.countermeasures import ( @@ -6,7 +9,7 @@ from pyecsca.ec.countermeasures import ( MultiplicativeSplitting, EuclideanSplitting, ) -from pyecsca.ec.mult import LTRMultiplier +from pyecsca.ec.mult import * @pytest.fixture(params=["add-1998-cmo-2", "add-2015-rcb"]) @@ -20,81 +23,199 @@ def dbl(secp128r1, request): @pytest.fixture() -def mult(secp128r1, add, dbl): - return LTRMultiplier(add, dbl, complete=False) +def mults(secp128r1, add, dbl): + neg = secp128r1.curve.coordinate_model.formulas["neg"] + scale = secp128r1.curve.coordinate_model.formulas["z"] + + ltr_options = { + "always": (True, False), + "complete": (True, False), + "accumulation_order": tuple(AccumulationOrder), + } + ltrs = [ + LTRMultiplier(add, dbl, scale, **dict(zip(ltr_options.keys(), combination))) + for combination in product(*ltr_options.values()) + ] + rtl_options = ltr_options + rtls = [ + RTLMultiplier(add, dbl, scale, **dict(zip(rtl_options.keys(), combination))) + for combination in product(*rtl_options.values()) + ] + bnaf_options = { + "direction": tuple(ProcessingDirection), + "accumulation_order": tuple(AccumulationOrder), + } + bnafs = [ + BinaryNAFMultiplier( + add, dbl, neg, scale, **dict(zip(bnaf_options.keys(), combination)) + ) + for combination in product(*bnaf_options.values()) + ] + wnaf_options = { + "precompute_negation": (True, False), + "width": (3, 5), + "accumulation_order": tuple(AccumulationOrder), + } + wnafs = [ + WindowNAFMultiplier( + add, dbl, neg, scl=scale, **dict(zip(wnaf_options.keys(), combination)) + ) + for combination in product(*wnaf_options.values()) + ] + booth_options = { + "precompute_negation": (True, False), + "width": (3, 5), + "accumulation_order": tuple(AccumulationOrder), + } + booths = [ + WindowBoothMultiplier( + add, dbl, neg, scl=scale, **dict(zip(booth_options.keys(), combination)) + ) + for combination in product(*booth_options.values()) + ] + ladder_options = {"complete": (True, False)} + ladders = [ + SimpleLadderMultiplier( + add, dbl, scale, **dict(zip(ladder_options.keys(), combination)) + ) + for combination in product(*ladder_options.values()) + ] + fixed_options = {"m": (5, 8), "accumulation_order": tuple(AccumulationOrder)} + fixeds = [ + FixedWindowLTRMultiplier( + add, dbl, scl=scale, **dict(zip(fixed_options.keys(), combination)) + ) + for combination in product(*fixed_options.values()) + ] + sliding_options = { + "width": (3, 5), + "recoding_direction": tuple(ProcessingDirection), + "accumulation_order": tuple(AccumulationOrder), + } + slides = [ + SlidingWindowMultiplier( + add, dbl, scl=scale, **dict(zip(sliding_options.keys(), combination)) + ) + for combination in product(*sliding_options.values()) + ] + precomp_options = { + "always": (True, False), + "complete": (True, False), + "direction": tuple(ProcessingDirection), + "accumulation_order": tuple(AccumulationOrder), + } + precomps = [ + FullPrecompMultiplier( + add, dbl, scl=scale, **dict(zip(precomp_options.keys(), combination)) + ) + for combination in product(*precomp_options.values()) + ] + bgmw_options = { + "width": (2, 3, 5), + "direction": tuple(ProcessingDirection), + "accumulation_order": tuple(AccumulationOrder), + } + bgmws = [ + BGMWMultiplier( + add, dbl, scl=scale, **dict(zip(bgmw_options.keys(), combination)) + ) + for combination in product(*bgmw_options.values()) + ] + comb_options = {"width": (2, 3, 4, 5), "accumulation_order": tuple(AccumulationOrder)} + combs = [ + CombMultiplier( + add, dbl, scl=scale, **dict(zip(comb_options.keys(), combination)) + ) + for combination in product(*comb_options.values()) + ] + + return ( + ltrs + + rtls + + bnafs + + wnafs + + booths + + [CoronMultiplier(add, dbl, scale)] + + ladders + + fixeds + + slides + + precomps + + bgmws + + combs + ) @pytest.mark.parametrize( "num", [ - 325385790209017329644351321912443757746, - 123456789314159265358979323846264338327, - 987654321314159265358979323846264338327, - 786877845665557891354654531354008066400, + 3253857902090173296443513219124437746, + 1234567893141592653589793238464338327, ], ) -def test_group_scalar_rand(mult, secp128r1, num): +def test_group_scalar_rand(mults, secp128r1, num): + mult = copy(mults[0]) mult.init(secp128r1, secp128r1.generator) raw = mult.multiply(num) - gsr = GroupScalarRandomization(mult) - gsr.init(secp128r1, secp128r1.generator) - masked = gsr.multiply(num) - assert raw.equals(masked) + for mult in mults: + gsr = GroupScalarRandomization(mult) + gsr.init(secp128r1, secp128r1.generator) + masked = gsr.multiply(num) + assert raw.equals(masked) @pytest.mark.parametrize( "num", [ - 325385790209017329644351321912443757746, - 123456789314159265358979323846264338327, - 987654321314159265358979323846264338327, - 786877845665557891354654531354008066400, + 3253857902090173296443513219124437746, + 1234567893141592653589793238464338327, ], ) -def test_additive_splitting(mult, secp128r1, num): +def test_additive_splitting(mults, secp128r1, num): + mult = copy(mults[0]) mult.init(secp128r1, secp128r1.generator) raw = mult.multiply(num) - asplit = AdditiveSplitting(mult) - asplit.init(secp128r1, secp128r1.generator) - masked = asplit.multiply(num) - assert raw.equals(masked) + for mult in mults: + asplit = AdditiveSplitting(mult) + asplit.init(secp128r1, secp128r1.generator) + masked = asplit.multiply(num) + assert raw.equals(masked) @pytest.mark.parametrize( "num", [ - 325385790209017329644351321912443757746, - 123456789314159265358979323846264338327, - 987654321314159265358979323846264338327, - 786877845665557891354654531354008066400, + 3253857902090173296443513219124437746, + 1234567893141592653589793238464338327, ], ) -def test_multiplicative_splitting(mult, secp128r1, num): +def test_multiplicative_splitting(mults, secp128r1, num): + mult = copy(mults[0]) mult.init(secp128r1, secp128r1.generator) raw = mult.multiply(num) - msplit = MultiplicativeSplitting(mult) - msplit.init(secp128r1, secp128r1.generator) - masked = msplit.multiply(num) - assert raw.equals(masked) + for mult in mults: + msplit = MultiplicativeSplitting(mult) + msplit.init(secp128r1, secp128r1.generator) + masked = msplit.multiply(num) + assert raw.equals(masked) @pytest.mark.parametrize( "num", [ - 325385790209017329644351321912443757746, - 123456789314159265358979323846264338327, - 987654321314159265358979323846264338327, - 786877845665557891354654531354008066400, + 3253857902090173296443513219124437746, + 1234567893141592653589793238464338327, ], ) -def test_euclidean_splitting(mult, secp128r1, num): +def test_euclidean_splitting(mults, secp128r1, num): + mult = copy(mults[0]) mult.init(secp128r1, secp128r1.generator) raw = mult.multiply(num) - esplit = EuclideanSplitting(mult) - esplit.init(secp128r1, secp128r1.generator) - masked = esplit.multiply(num) - assert raw.equals(masked) + for mult in mults: + esplit = EuclideanSplitting(mult) + esplit.init(secp128r1, secp128r1.generator) + masked = esplit.multiply(num) + assert raw.equals(masked) diff --git a/test/ec/test_mult.py b/test/ec/test_mult.py index 721badc..e915585 100644 --- a/test/ec/test_mult.py +++ b/test/ec/test_mult.py @@ -361,7 +361,7 @@ def dbl(secp128r1, request): @pytest.mark.parametrize( - "num", [10, 2355498743, 325385790209017329644351321912443757746] + "num", [10, 2355498743, 3253857902090173296443513219124437746] ) def test_basic_multipliers(secp128r1, num, add, dbl): neg = secp128r1.curve.coordinate_model.formulas["neg"] @@ -451,7 +451,7 @@ def test_basic_multipliers(secp128r1, num, add, dbl): for combination in product(*precomp_options.values()) ] bgmw_options = { - "width": (3, 5), + "width": (2, 3, 5), "direction": tuple(ProcessingDirection), "accumulation_order": tuple(AccumulationOrder), } @@ -461,7 +461,7 @@ def test_basic_multipliers(secp128r1, num, add, dbl): ) for combination in product(*bgmw_options.values()) ] - comb_options = {"width": (2, 3, 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)) |
