diff options
| author | J08nY | 2023-10-04 17:32:51 +0200 |
|---|---|---|
| committer | J08nY | 2023-10-04 17:32:51 +0200 |
| commit | b22af4813f19427e22f0f22aa9c682a03b8da257 (patch) | |
| tree | 9fbbf93b44145c1090d1b3e6b48113ac204814bd /pyecsca/ec/mult/fixed.py | |
| parent | bff6dc1e9777000d9a1867ce3f0863cbb3900896 (diff) | |
| download | pyecsca-b22af4813f19427e22f0f22aa9c682a03b8da257.tar.gz pyecsca-b22af4813f19427e22f0f22aa9c682a03b8da257.tar.zst pyecsca-b22af4813f19427e22f0f22aa9c682a03b8da257.zip | |
Add fullprecomp multiplier.
Diffstat (limited to 'pyecsca/ec/mult/fixed.py')
| -rw-r--r-- | pyecsca/ec/mult/fixed.py | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/pyecsca/ec/mult/fixed.py b/pyecsca/ec/mult/fixed.py new file mode 100644 index 0000000..cd44e3a --- /dev/null +++ b/pyecsca/ec/mult/fixed.py @@ -0,0 +1,117 @@ +"""Provides fixed-base scalar multipliers that do a lot of pre-computation (but not combs).""" +from copy import copy +from typing import MutableMapping, Optional + +from public import public + +from pyecsca.ec.formula import AdditionFormula, DoublingFormula, ScalingFormula +from pyecsca.ec.mult import AccumulatorMultiplier, ScalarMultiplier, ProcessingDirection, AccumulationOrder, \ + PrecomputationAction, ScalarMultiplicationAction +from pyecsca.ec.params import DomainParameters +from pyecsca.ec.point import Point + + +@public +class FullPrecompMultiplier(AccumulatorMultiplier, ScalarMultiplier): + """ + See page 104 of [GECC]_: + + For example, if the points `[2]P,[2^2]P,...,[2^t−1]P` are precomputed, then the right-to-left + binary method (Algorithm 3.26) has expected running time `(m/2)A` (all doublings are + eliminated). + + :param always: Whether the addition is always performed. + :param direction: Whether it is LTR or RTL. + :param complete: Whether it starts processing at full order-bit-length. + """ + + requires = {AdditionFormula, DoublingFormula} + optionals = {ScalingFormula} + always: bool + """Whether the double and add always method is used.""" + direction: ProcessingDirection + """Whether it is LTR or RTL.""" + complete: bool + """Whether it starts processing at full order-bit-length.""" + _points: MutableMapping[int, Point] + + def __init__( + self, + add: AdditionFormula, + dbl: DoublingFormula, + scl: Optional[ScalingFormula] = None, + always: bool = False, + direction: ProcessingDirection = ProcessingDirection.LTR, + accumulation_order: AccumulationOrder = AccumulationOrder.PeqPR, + complete: bool = True, + short_circuit: bool = True, + ): + super().__init__( + short_circuit=short_circuit, accumulation_order=accumulation_order, add=add, dbl=dbl, scl=scl + ) + self.always = always + self.direction = direction + self.complete = complete + + def __hash__(self): + return id(self) + + def __eq__(self, other): + if not isinstance(other, FullPrecompMultiplier): + return False + return self.formulas == other.formulas and self.short_circuit == other.short_circuit and self.direction == other.direction and self.accumulation_order == other.accumulation_order and self.always == other.always and self.complete == other.complete + + def __repr__(self): + return f"{self.__class__.__name__}({tuple(self.formulas.values())}, short_circuit={self.short_circuit}, accumulation_order={self.accumulation_order}, always={self.always}, complete={self.complete})" + + def init(self, params: DomainParameters, point: Point): + with PrecomputationAction(params, point): + super().init(params, point) + self._points = {} + current_point = point + for i in range(params.order.bit_length() + 1): + self._points[i] = current_point + current_point = self._dbl(current_point) + + def _ltr(self, scalar: int) -> Point: + if self.complete: + top = self._params.order.bit_length() - 1 + else: + top = scalar.bit_length() - 1 + r = copy(self._params.curve.neutral) + for i in range(top, -1, -1): + if scalar & (1 << i) != 0: + r = self._accumulate(r, self._points[i]) + elif self.always: + # dummy add + self._accumulate(r, self._points[i]) + return r + + def _rtl(self, scalar: int) -> Point: + r = copy(self._params.curve.neutral) + if self.complete: + top = self._params.order.bit_length() + else: + top = scalar.bit_length() + for i in range(top): + if scalar & 1 != 0: + r = self._accumulate(r, self._points[i]) + elif self.always: + # dummy add + self._accumulate(r, self._points[i]) + scalar >>= 1 + return r + + def multiply(self, scalar: int) -> Point: + if not self._initialized: + raise ValueError("ScalarMultiplier not initialized.") + with ScalarMultiplicationAction(self._point, scalar) as action: + if scalar == 0: + return action.exit(copy(self._params.curve.neutral)) + if self.direction is ProcessingDirection.LTR: + r = self._ltr(scalar) + elif self.direction is ProcessingDirection.RTL: + r = self._rtl(scalar) + if "scl" in self.formulas: + r = self._scl(r) + return action.exit(r) |
