aboutsummaryrefslogtreecommitdiff
path: root/pyecsca/ec/mult/binary.py
diff options
context:
space:
mode:
authorJ08nY2023-08-27 12:14:21 +0200
committerJ08nY2023-08-27 12:14:21 +0200
commit19bc2ed369cbcd41c08132edb74d7a1afd843962 (patch)
treefb475e74b33bc78e1492a4a51cd19dd442fa0cee /pyecsca/ec/mult/binary.py
parent578f01131017f71dd56f0385c4c8961d2c56ccdc (diff)
downloadpyecsca-19bc2ed369cbcd41c08132edb74d7a1afd843962.tar.gz
pyecsca-19bc2ed369cbcd41c08132edb74d7a1afd843962.tar.zst
pyecsca-19bc2ed369cbcd41c08132edb74d7a1afd843962.zip
Diffstat (limited to 'pyecsca/ec/mult/binary.py')
-rw-r--r--pyecsca/ec/mult/binary.py207
1 files changed, 207 insertions, 0 deletions
diff --git a/pyecsca/ec/mult/binary.py b/pyecsca/ec/mult/binary.py
new file mode 100644
index 0000000..dda3388
--- /dev/null
+++ b/pyecsca/ec/mult/binary.py
@@ -0,0 +1,207 @@
+from abc import ABC
+from copy import copy
+from typing import Optional
+
+from public import public
+
+from .base import ScalarMultiplier, ProcessingDirection, AccumulationOrder, ScalarMultiplicationAction
+from ..formula import (
+ AdditionFormula,
+ DoublingFormula,
+ ScalingFormula,
+)
+from ..point import Point
+
+
+@public
+class DoubleAndAddMultiplier(ScalarMultiplier, ABC):
+ """
+ Classic double and add scalar multiplication algorithm.
+
+ :param always: Whether the double and add always method is used.
+ :param direction: Whether it is LTR or RTL.
+ :param accumulation_order: The order of accumulation of points.
+ :param complete: (Only for LTR, always false for RTL) Whether it starts processing at full order-bit-length.
+ """
+ requires = {AdditionFormula, DoublingFormula}
+ optionals = {ScalingFormula}
+ always: bool
+ direction: ProcessingDirection
+ accumulation_order: AccumulationOrder
+ complete: bool
+
+ 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, add=add, dbl=dbl, scl=scl)
+ self.always = always
+ self.direction = direction
+ self.accumulation_order = accumulation_order
+ self.complete = complete
+
+ def __hash__(self):
+ return id(self)
+
+ def __eq__(self, other):
+ if not isinstance(other, DoubleAndAddMultiplier):
+ 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 _accumulate(self, p: Point, r: Point) -> Point:
+ if self.accumulation_order is AccumulationOrder.PeqPR:
+ p = self._add(p, r)
+ elif self.accumulation_order is AccumulationOrder.PeqRP:
+ p = self._add(r, p)
+ return p
+
+ def _ltr(self, scalar: int) -> Point:
+ if self.complete:
+ q = self._point
+ r = copy(self._params.curve.neutral)
+ top = self._params.order.bit_length() - 1
+ else:
+ q = copy(self._point)
+ r = copy(self._point)
+ top = scalar.bit_length() - 2
+ for i in range(top, -1, -1):
+ r = self._dbl(r)
+ if scalar & (1 << i) != 0:
+ r = self._accumulate(r, q)
+ elif self.always:
+ # dummy add
+ self._accumulate(r, q)
+ return r
+
+ def _rtl(self, scalar: int) -> Point:
+ q = self._point
+ r = copy(self._params.curve.neutral)
+ if self.complete:
+ top = self._params.order.bit_length()
+ else:
+ top = scalar.bit_length()
+ for _ in range(top):
+ if scalar & 1 != 0:
+ r = self._accumulate(r, q)
+ elif self.always:
+ # dummy add
+ self._accumulate(r, q)
+ # TODO: This double is unnecessary in the last iteration.
+ q = self._dbl(q)
+ 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)
+
+
+@public
+class LTRMultiplier(DoubleAndAddMultiplier):
+ """
+ Classic double and add scalar multiplication algorithm, that scans the scalar left-to-right (msb to lsb).
+ """
+
+ def __init__(
+ self,
+ add: AdditionFormula,
+ dbl: DoublingFormula,
+ scl: Optional[ScalingFormula] = None,
+ always: bool = False,
+ accumulation_order: AccumulationOrder = AccumulationOrder.PeqPR,
+ complete: bool = True,
+ short_circuit: bool = True,
+ ):
+ super().__init__(short_circuit=short_circuit, direction=ProcessingDirection.LTR,
+ accumulation_order=accumulation_order, always=always, complete=complete,
+ add=add, dbl=dbl, scl=scl)
+
+
+@public
+class RTLMultiplier(DoubleAndAddMultiplier):
+ """
+ Classic double and add scalar multiplication algorithm, that scans the scalar right-to-left (lsb to msb).
+ """
+
+ def __init__(
+ self,
+ add: AdditionFormula,
+ dbl: DoublingFormula,
+ scl: Optional[ScalingFormula] = None,
+ always: bool = False,
+ accumulation_order: AccumulationOrder = AccumulationOrder.PeqPR,
+ complete: bool = True,
+ short_circuit: bool = True,
+ ):
+ super().__init__(short_circuit=short_circuit, direction=ProcessingDirection.RTL,
+ accumulation_order=accumulation_order, always=always,
+ add=add, dbl=dbl, scl=scl, complete=complete)
+
+
+@public
+class CoronMultiplier(ScalarMultiplier):
+ """
+ Coron's double and add resistant against SPA.
+
+ From:
+ **Resistance against Differential Power Analysis for Elliptic Curve Cryptosystems**
+
+ https://link.springer.com/content/pdf/10.1007/3-540-48059-5_25.pdf
+ """
+
+ requires = {AdditionFormula, DoublingFormula}
+ optionals = {ScalingFormula}
+
+ def __init__(
+ self,
+ add: AdditionFormula,
+ dbl: DoublingFormula,
+ scl: Optional[ScalingFormula] = None,
+ short_circuit: bool = True,
+ ):
+ super().__init__(short_circuit=short_circuit, add=add, dbl=dbl, scl=scl)
+
+ def __hash__(self):
+ return id(self)
+
+ def __eq__(self, other):
+ if not isinstance(other, CoronMultiplier):
+ return False
+ return self.formulas == other.formulas and self.short_circuit == other.short_circuit
+
+ 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))
+ q = self._point
+ p0 = copy(q)
+ for i in range(scalar.bit_length() - 2, -1, -1):
+ p0 = self._dbl(p0)
+ p1 = self._add(p0, q)
+ if scalar & (1 << i) != 0:
+ p0 = p1
+ if "scl" in self.formulas:
+ p0 = self._scl(p0)
+ return action.exit(p0)