aboutsummaryrefslogtreecommitdiffhomepage
path: root/pyecsca
diff options
context:
space:
mode:
authorAndrej Bátora2023-10-05 14:45:06 +0200
committerGitHub2023-10-05 14:45:06 +0200
commit382a5896488f033c8269cf81fcdde3bf1ef98cad (patch)
tree6f3ffa714b611b205ecfb6a1399dd8d97ddd393f /pyecsca
parent28d3448cec3599559a04e3f0ebe8d175bf6385d1 (diff)
parent9b571fdf84732419b52e0465d8354f9d8b6794e5 (diff)
downloadpyecsca-382a5896488f033c8269cf81fcdde3bf1ef98cad.tar.gz
pyecsca-382a5896488f033c8269cf81fcdde3bf1ef98cad.tar.zst
pyecsca-382a5896488f033c8269cf81fcdde3bf1ef98cad.zip
Merge branch 'J08nY:master' into CPA_pyecsca
Diffstat (limited to 'pyecsca')
-rw-r--r--pyecsca/ec/mult/__init__.py2
-rw-r--r--pyecsca/ec/mult/base.py24
-rw-r--r--pyecsca/ec/mult/binary.py31
-rw-r--r--pyecsca/ec/mult/comb.py183
-rw-r--r--pyecsca/ec/mult/fixed.py119
-rw-r--r--pyecsca/ec/mult/ladder.py39
-rw-r--r--pyecsca/ec/mult/naf.py31
-rw-r--r--pyecsca/ec/mult/window.py33
-rw-r--r--pyecsca/sca/re/rpa.py5
-rw-r--r--pyecsca/sca/re/zvp.py5
10 files changed, 446 insertions, 26 deletions
diff --git a/pyecsca/ec/mult/__init__.py b/pyecsca/ec/mult/__init__.py
index 419e5ff..14f1c99 100644
--- a/pyecsca/ec/mult/__init__.py
+++ b/pyecsca/ec/mult/__init__.py
@@ -2,6 +2,8 @@
from .base import *
from .binary import *
+from .comb import *
+from .fixed import *
from .ladder import *
from .naf import *
from .window import *
diff --git a/pyecsca/ec/mult/base.py b/pyecsca/ec/mult/base.py
index a0584d1..5bfef73 100644
--- a/pyecsca/ec/mult/base.py
+++ b/pyecsca/ec/mult/base.py
@@ -1,3 +1,6 @@
+"""Provides (mostly abstract) base classes for scalar multipliers, enums used to specify their parameters
+and actions used in them."""
+
from abc import ABC, abstractmethod
from copy import copy
from enum import Enum
@@ -71,9 +74,9 @@ class ScalarMultiplier(ABC):
"""
requires: ClassVar[Set[Type]] # Type[Formula] but mypy has a false positive
- """The set of formulas that the multiplier requires."""
+ """The set of formula types that the multiplier requires."""
optionals: ClassVar[Set[Type]] # Type[Formula] but mypy has a false positive
- """The optional set of formulas that the multiplier can use."""
+ """The optional set of formula types that the multiplier can use."""
short_circuit: bool
"""Whether the formulas will short-circuit upon input of the point at infinity."""
formulas: Mapping[str, Formula]
@@ -93,9 +96,23 @@ class ScalarMultiplier(ABC):
)
!= 1
):
- raise ValueError
+ raise ValueError("Formulas need to belong to the same coordinate model.")
self.short_circuit = short_circuit
self.formulas = {k: v for k, v in formulas.items() if v is not None}
+ found_required = set()
+ for formula in self.formulas.values():
+ for required in self.requires:
+ if isinstance(formula, required):
+ found_required.add(required)
+ break
+ else:
+ for optional in self.optionals:
+ if isinstance(formula, optional):
+ break
+ else:
+ raise ValueError("Not required or optional formulas provided.")
+ if found_required != self.requires:
+ raise ValueError("Required formulas missing.")
def _add(self, one: Point, other: Point) -> Point:
if "add" not in self.formulas:
@@ -216,6 +233,7 @@ class AccumulatorMultiplier(ScalarMultiplier, ABC):
:param accumulation_order: The order of accumulation of points.
"""
accumulation_order: AccumulationOrder
+ """The order of accumulation of points."""
def __init__(self, *args, accumulation_order: AccumulationOrder = AccumulationOrder.PeqPR, **kwargs):
super().__init__(*args, **kwargs)
diff --git a/pyecsca/ec/mult/binary.py b/pyecsca/ec/mult/binary.py
index 6145828..f8acb16 100644
--- a/pyecsca/ec/mult/binary.py
+++ b/pyecsca/ec/mult/binary.py
@@ -1,3 +1,4 @@
+"""Provides binary scalar multipliers (LTR and RTL), that process the scalar as-is, bit-by-bit."""
from abc import ABC
from copy import copy
from typing import Optional
@@ -19,16 +20,24 @@ class DoubleAndAddMultiplier(AccumulatorMultiplier, ScalarMultiplier, ABC):
"""
Classic double and add scalar multiplication algorithm.
+ .. note::
+ This is an ABC, you should use the `LTRMultiplier` and `RTLMultiplier` classes.
+
+ :param short_circuit: Whether the use of formulas will be guarded by short-circuit on inputs
+ of the point at infinity.
: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.
+ :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."""
def __init__(
self,
@@ -55,7 +64,7 @@ class DoubleAndAddMultiplier(AccumulatorMultiplier, ScalarMultiplier, ABC):
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})"
+ return f"{self.__class__.__name__}({tuple(self.formulas.values())}, short_circuit={self.short_circuit}, direction={self.direction}, accumulation_order={self.accumulation_order}, always={self.always}, complete={self.complete})"
def _ltr(self, scalar: int) -> Point:
if self.complete:
@@ -112,6 +121,12 @@ class DoubleAndAddMultiplier(AccumulatorMultiplier, ScalarMultiplier, ABC):
class LTRMultiplier(DoubleAndAddMultiplier):
"""
Classic double and add scalar multiplication algorithm, that scans the scalar left-to-right (msb to lsb).
+
+ :param short_circuit: Whether the use of formulas will be guarded by short-circuit on inputs
+ of the point at infinity.
+ :param always: Whether the double and add always method is used.
+ :param accumulation_order: The order of accumulation of points.
+ :param complete: Whether it starts processing at full order-bit-length.
"""
def __init__(
@@ -133,6 +148,12 @@ class LTRMultiplier(DoubleAndAddMultiplier):
class RTLMultiplier(DoubleAndAddMultiplier):
"""
Classic double and add scalar multiplication algorithm, that scans the scalar right-to-left (lsb to msb).
+
+ :param short_circuit: Whether the use of formulas will be guarded by short-circuit on inputs
+ of the point at infinity.
+ :param always: Whether the double and add always method is used.
+ :param accumulation_order: The order of accumulation of points.
+ :param complete: Whether it starts processing at full order-bit-length.
"""
def __init__(
@@ -155,10 +176,10 @@ class CoronMultiplier(ScalarMultiplier):
"""
Coron's double and add resistant against SPA.
- From:
- **Resistance against Differential Power Analysis for Elliptic Curve Cryptosystems**
+ From [CO2002]_.
- https://link.springer.com/content/pdf/10.1007/3-540-48059-5_25.pdf
+ :param short_circuit: Whether the use of formulas will be guarded by short-circuit on inputs
+ of the point at infinity.
"""
requires = {AdditionFormula, DoublingFormula}
diff --git a/pyecsca/ec/mult/comb.py b/pyecsca/ec/mult/comb.py
new file mode 100644
index 0000000..8d51806
--- /dev/null
+++ b/pyecsca/ec/mult/comb.py
@@ -0,0 +1,183 @@
+"""Provides Comb-like scalar multipliers, such as BGMW or Lim-Lee."""
+from copy import copy
+from math import ceil
+from typing import MutableMapping, Optional
+
+from public import public
+
+from ..formula import AdditionFormula, DoublingFormula, ScalingFormula
+from ..mult import (
+ AccumulatorMultiplier,
+ ScalarMultiplier,
+ ProcessingDirection,
+ AccumulationOrder,
+ PrecomputationAction,
+ ScalarMultiplicationAction,
+)
+from ..params import DomainParameters
+from ..point import Point
+from ..scalar import convert_base
+
+
+@public
+class BGMWMultiplier(AccumulatorMultiplier, ScalarMultiplier):
+ """
+ Brickell, Gordon, McCurley and Wilson (BGMW) scalar multiplier,
+ or rather, its one parametrization.
+
+ Algorithm 3.41 from [GECC]_
+
+ :param width: Window width.
+ :param direction: Whether it is LTR or RTL.
+ :param accumulation_order: The order of accumulation of points.
+ """
+
+ requires = {AdditionFormula, DoublingFormula}
+ optionals = {ScalingFormula}
+ direction: ProcessingDirection
+ """Whether it is LTR or RTL."""
+ width: int
+ """Window width."""
+ _points: MutableMapping[int, Point]
+
+ def __init__(
+ self,
+ add: AdditionFormula,
+ dbl: DoublingFormula,
+ width: int,
+ scl: Optional[ScalingFormula] = None,
+ direction: ProcessingDirection = ProcessingDirection.LTR,
+ accumulation_order: AccumulationOrder = AccumulationOrder.PeqPR,
+ short_circuit: bool = True,
+ ):
+ super().__init__(
+ short_circuit=short_circuit,
+ accumulation_order=accumulation_order,
+ add=add,
+ dbl=dbl,
+ scl=scl,
+ )
+ self.direction = direction
+ self.width = width
+
+ def init(self, params: DomainParameters, point: Point):
+ with PrecomputationAction(params, point):
+ super().init(params, point)
+ d = ceil(params.order.bit_length() / self.width)
+ self._points = {}
+ current_point = point
+ for i in range(d):
+ self._points[i] = current_point
+ if i != d - 1:
+ for _ in range(self.width):
+ current_point = self._dbl(current_point)
+
+ 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))
+ a = copy(self._params.curve.neutral)
+ b = copy(self._params.curve.neutral)
+ recoded = convert_base(scalar, 2**self.width)
+ for j in range(2**self.width - 1, 0, -1):
+ if self.direction == ProcessingDirection.RTL:
+ for i, ki in enumerate(recoded):
+ if ki == j:
+ b = self._accumulate(b, self._points[i])
+ elif self.direction == ProcessingDirection.LTR:
+ for i, ki in reversed(list(enumerate(recoded))):
+ if ki == j:
+ b = self._accumulate(b, self._points[i])
+ if self.short_circuit and a == b:
+ # TODO: Double necessary here for incomplete formulas, maybe another param and not reuse short_cirtuit?
+ a = self._dbl(b)
+ else:
+ a = self._accumulate(a, b)
+ if "scl" in self.formulas:
+ a = self._scl(a)
+ return action.exit(a)
+
+
+@public
+class CombMultiplier(AccumulatorMultiplier, ScalarMultiplier):
+ """
+ Comb multiplier.
+
+ Algorithm 3.44 from [GECC]_
+
+ :param width: Window width (number of comb teeth).
+ :param accumulation_order: The order of accumulation of points.
+ """
+
+ requires = {AdditionFormula, DoublingFormula}
+ optionals = {ScalingFormula}
+ width: int
+ """Window width."""
+ _points: MutableMapping[int, Point]
+
+ def __init__(
+ self,
+ add: AdditionFormula,
+ dbl: DoublingFormula,
+ width: int,
+ scl: Optional[ScalingFormula] = None,
+ accumulation_order: AccumulationOrder = AccumulationOrder.PeqPR,
+ short_circuit: bool = True,
+ ):
+ super().__init__(
+ short_circuit=short_circuit,
+ accumulation_order=accumulation_order,
+ add=add,
+ dbl=dbl,
+ scl=scl,
+ )
+ self.width = width
+
+ def init(self, params: DomainParameters, point: Point):
+ with PrecomputationAction(params, point):
+ super().init(params, point)
+ d = ceil(params.order.bit_length() / self.width)
+ base_points = {}
+ current_point = point
+ for i in range(self.width):
+ base_points[i] = current_point
+ if i != d - 1:
+ for _ in range(d):
+ current_point = self._dbl(current_point)
+ self._points = {}
+ for j in range(1, 2**self.width):
+ points = []
+ for i in range(self.width):
+ if j & (1 << i):
+ points.append(base_points[i])
+ self._points[j] = points[0]
+ for other in points[1:]:
+ self._points[j] = self._accumulate(self._points[j], other)
+
+ 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 = copy(self._params.curve.neutral)
+ d = ceil(self._params.order.bit_length() / self.width)
+ recoded = convert_base(scalar, 2**d)
+ if len(recoded) != self.width:
+ recoded.extend([0] * (self.width - len(recoded)))
+ for i in range(d - 1, -1, -1):
+ q = self._dbl(q)
+ word = 0
+ for j in range(self.width):
+ # get i-th bit of recoded[j] and set it into the j-th bit of word
+ bit = (recoded[j] >> i) & 1
+ word |= bit << j
+ if word:
+ q = self._accumulate(q, self._points[word])
+ # TODO always
+
+ if "scl" in self.formulas:
+ q = self._scl(q)
+ return action.exit(q)
diff --git a/pyecsca/ec/mult/fixed.py b/pyecsca/ec/mult/fixed.py
new file mode 100644
index 0000000..010767b
--- /dev/null
+++ b/pyecsca/ec/mult/fixed.py
@@ -0,0 +1,119 @@
+"""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 ..formula import AdditionFormula, DoublingFormula, ScalingFormula
+from ..mult import AccumulatorMultiplier, ScalarMultiplier, ProcessingDirection, AccumulationOrder, \
+ PrecomputationAction, ScalarMultiplicationAction
+from ..params import DomainParameters
+from ..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 accumulation_order: The order of accumulation of points.
+ :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
+ if i != params.order.bit_length():
+ 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)
diff --git a/pyecsca/ec/mult/ladder.py b/pyecsca/ec/mult/ladder.py
index ad90730..2fc1244 100644
--- a/pyecsca/ec/mult/ladder.py
+++ b/pyecsca/ec/mult/ladder.py
@@ -1,3 +1,4 @@
+"""Provides ladder-based scalar multipliers, either using the ladder formula, or (diffadd, dbl) or (add, dbl)."""
from copy import copy
from typing import Optional
from public import public
@@ -15,11 +16,20 @@ from ..point import Point
@public
class LadderMultiplier(ScalarMultiplier):
- """Montgomery ladder multiplier, using a three input, two output ladder formula."""
+ """
+ Montgomery ladder multiplier, using a three input, two output ladder formula.
+
+ Optionally takes a doubling formula, and if `complete` is false, it requires one.
+
+ :param short_circuit: Whether the use of formulas will be guarded by short-circuit on inputs
+ of the point at infinity.
+ :param complete: Whether it starts processing at full order-bit-length.
+ """
requires = {LadderFormula}
optionals = {DoublingFormula, ScalingFormula}
complete: bool
+ """Whether it starts processing at full order-bit-length."""
def __init__(
self,
@@ -42,6 +52,9 @@ class LadderMultiplier(ScalarMultiplier):
return False
return self.formulas == other.formulas and self.short_circuit == other.short_circuit and self.complete == other.complete
+ def __repr__(self):
+ return f"{self.__class__.__name__}({tuple(self.formulas.values())}, short_circuit={self.short_circuit}, complete={self.complete})"
+
def multiply(self, scalar: int) -> Point:
if not self._initialized:
raise ValueError("ScalarMultiplier not initialized.")
@@ -69,11 +82,18 @@ class LadderMultiplier(ScalarMultiplier):
@public
class SimpleLadderMultiplier(ScalarMultiplier):
- """Montgomery ladder multiplier, using addition and doubling formulas."""
+ """
+ Montgomery ladder multiplier, using addition and doubling formulas.
+
+ :param short_circuit: Whether the use of formulas will be guarded by short-circuit on inputs
+ of the point at infinity.
+ :param complete: Whether it starts processing at full order-bit-length.
+ """
requires = {AdditionFormula, DoublingFormula}
optionals = {ScalingFormula}
complete: bool
+ """Whether it starts processing at full order-bit-length."""
def __init__(
self,
@@ -94,6 +114,9 @@ class SimpleLadderMultiplier(ScalarMultiplier):
return False
return self.formulas == other.formulas and self.short_circuit == other.short_circuit and self.complete == other.complete
+ def __repr__(self):
+ return f"{self.__class__.__name__}({tuple(self.formulas.values())}, short_circuit={self.short_circuit}, complete={self.complete})"
+
def multiply(self, scalar: int) -> Point:
if not self._initialized:
raise ValueError("ScalarMultiplier not initialized.")
@@ -120,11 +143,18 @@ class SimpleLadderMultiplier(ScalarMultiplier):
@public
class DifferentialLadderMultiplier(ScalarMultiplier):
- """Montgomery ladder multiplier, using differential addition and doubling formulas."""
+ """
+ Montgomery ladder multiplier, using differential addition and doubling formulas.
+
+ :param short_circuit: Whether the use of formulas will be guarded by short-circuit on inputs
+ of the point at infinity.
+ :param complete: Whether it starts processing at full order-bit-length.
+ """
requires = {DifferentialAdditionFormula, DoublingFormula}
optionals = {ScalingFormula}
complete: bool
+ """Whether it starts processing at full order-bit-length."""
def __init__(
self,
@@ -145,6 +175,9 @@ class DifferentialLadderMultiplier(ScalarMultiplier):
return False
return self.formulas == other.formulas and self.short_circuit == other.short_circuit and self.complete == other.complete
+ def __repr__(self):
+ return f"{self.__class__.__name__}({tuple(self.formulas.values())}, short_circuit={self.short_circuit}, complete={self.complete})"
+
def multiply(self, scalar: int) -> Point:
if not self._initialized:
raise ValueError("ScalarMultiplier not initialized.")
diff --git a/pyecsca/ec/mult/naf.py b/pyecsca/ec/mult/naf.py
index 1cec8b0..9304475 100644
--- a/pyecsca/ec/mult/naf.py
+++ b/pyecsca/ec/mult/naf.py
@@ -1,3 +1,4 @@
+"""Provides scalar multipliers based on the Non Adjacent Form (NAF) recoding."""
from copy import copy
from typing import Optional, List, MutableMapping
from public import public
@@ -17,7 +18,14 @@ from ..scalar import naf, wnaf
@public
class BinaryNAFMultiplier(AccumulatorMultiplier, ScalarMultiplier):
- """Binary NAF (Non Adjacent Form) multiplier."""
+ """
+ Binary NAF (Non Adjacent Form) multiplier.
+
+ :param short_circuit: Whether the use of formulas will be guarded by short-circuit on inputs
+ of the point at infinity.
+ :param direction: Whether it is LTR or RTL.
+ :param accumulation_order: The order of accumulation of points.
+ """
requires = {AdditionFormula, DoublingFormula, NegationFormula}
optionals = {ScalingFormula}
@@ -45,7 +53,10 @@ class BinaryNAFMultiplier(AccumulatorMultiplier, ScalarMultiplier):
def __eq__(self, other):
if not isinstance(other, BinaryNAFMultiplier):
return False
- return self.formulas == other.formulas and self.short_circuit == other.short_circuit
+ return self.formulas == other.formulas and self.short_circuit == other.short_circuit and self.direction == other.direction and self.accumulation_order == other.accumulation_order
+
+ def __repr__(self):
+ return f"{self.__class__.__name__}({tuple(self.formulas.values())}, short_circuit={self.short_circuit}, direction={self.direction}, accumulation_order={self.accumulation_order})"
def init(self, params: DomainParameters, point: Point):
with PrecomputationAction(params, point):
@@ -93,14 +104,25 @@ class BinaryNAFMultiplier(AccumulatorMultiplier, ScalarMultiplier):
@public
class WindowNAFMultiplier(AccumulatorMultiplier, ScalarMultiplier):
- """Window NAF (Non Adjacent Form) multiplier, left-to-right."""
+ """
+ Window NAF (Non Adjacent Form) multiplier, left-to-right.
+
+ :param short_circuit: Whether the use of formulas will be guarded by short-circuit on inputs
+ of the point at infinity.
+ :param width: The width of the window.
+ :param accumulation_order: The order of accumulation of points.
+ :param precompute_negation: Whether to precompute the negation of the precomputed points as well.
+ It is computed on the fly otherwise.
+ """
requires = {AdditionFormula, DoublingFormula, NegationFormula}
optionals = {ScalingFormula}
_points: MutableMapping[int, Point]
_points_neg: MutableMapping[int, Point]
precompute_negation: bool = False
+ """Whether to precompute the negation of the precomputed points as well."""
width: int
+ """The width of the window."""
def __init__(
self,
@@ -127,6 +149,9 @@ class WindowNAFMultiplier(AccumulatorMultiplier, ScalarMultiplier):
return False
return self.formulas == other.formulas and self.short_circuit == other.short_circuit and self.width == other.width and self.precompute_negation == other.precompute_negation and self.accumulation_order == other.accumulation_order
+ def __repr__(self):
+ return f"{self.__class__.__name__}({tuple(self.formulas.values())}, short_circuit={self.short_circuit}, precompute_negation={self.precompute_negation}, accumulation_order={self.accumulation_order})"
+
def init(self, params: DomainParameters, point: Point):
with PrecomputationAction(params, point):
super().init(params, point)
diff --git a/pyecsca/ec/mult/window.py b/pyecsca/ec/mult/window.py
index 8b1ae2c..8317965 100644
--- a/pyecsca/ec/mult/window.py
+++ b/pyecsca/ec/mult/window.py
@@ -1,3 +1,4 @@
+"""Provides sliding window and fixed window scalar multipliers (including m-ary, for non power-of-2 m)."""
from copy import copy
from typing import Optional, MutableMapping
from public import public
@@ -16,13 +17,20 @@ from ..scalar import convert_base, sliding_window_rtl, sliding_window_ltr
@public
class SlidingWindowMultiplier(AccumulatorMultiplier, ScalarMultiplier):
- """Sliding window scalar multiplier."""
+ """
+ Sliding window scalar multiplier.
+
+ :param width: The width of the sliding-window recoding.
+ :param recoding_direction: The direction for the sliding-window recoding.
+ :param accumulation_order: The order of accumulation of points.
+ """
requires = {AdditionFormula, DoublingFormula}
optionals = {ScalingFormula}
- complete: bool
width: int
+ """The width of the sliding-window recoding."""
recoding_direction: ProcessingDirection
+ """The direction for the sliding-window recoding."""
_points: MutableMapping[int, Point]
def __init__(
@@ -49,6 +57,9 @@ class SlidingWindowMultiplier(AccumulatorMultiplier, ScalarMultiplier):
return False
return self.formulas == other.formulas and self.short_circuit == other.short_circuit and self.width == other.width and self.recoding_direction == other.recoding_direction and self.accumulation_order == other.accumulation_order
+ def __repr__(self):
+ return f"{self.__class__.__name__}({tuple(self.formulas.values())}, short_circuit={self.short_circuit}, width={self.width}, recoding_direction={self.recoding_direction}, accumulation_order={self.accumulation_order})"
+
def init(self, params: DomainParameters, point: Point):
with PrecomputationAction(params, point):
super().init(params, point)
@@ -81,12 +92,23 @@ class SlidingWindowMultiplier(AccumulatorMultiplier, ScalarMultiplier):
@public
class FixedWindowLTRMultiplier(AccumulatorMultiplier, ScalarMultiplier):
- """Like LTRMultiplier, but not binary, but m-ary."""
+ """
+ Like LTRMultiplier, but m-ary, not binary.
+
+ For `m` a power-of-2 this is a fixed window multiplier
+ that works on `log_2(m)` wide windows and uses only doublings
+ to perform the multiplication-by-m between each window addition.
+
+ For other `m` values, this is the m-ary multiplier.
+
+ :param m: The arity of the multiplier.
+ :param accumulation_order: The order of accumulation of points.
+ """
requires = {AdditionFormula, DoublingFormula}
optionals = {ScalingFormula}
- complete: bool
m: int
+ """The arity of the multiplier."""
_points: MutableMapping[int, Point]
def __init__(
@@ -114,6 +136,9 @@ class FixedWindowLTRMultiplier(AccumulatorMultiplier, ScalarMultiplier):
return False
return self.formulas == other.formulas and self.short_circuit == other.short_circuit and self.m == other.m and self.accumulation_order == other.accumulation_order
+ def __repr__(self):
+ return f"{self.__class__.__name__}({tuple(self.formulas.values())}, short_circuit={self.short_circuit}, m={self.m}, accumulation_order={self.accumulation_order})"
+
def init(self, params: DomainParameters, point: Point):
with PrecomputationAction(params, point):
super().init(params, point)
diff --git a/pyecsca/sca/re/rpa.py b/pyecsca/sca/re/rpa.py
index 5a44a0f..5459ff2 100644
--- a/pyecsca/sca/re/rpa.py
+++ b/pyecsca/sca/re/rpa.py
@@ -1,8 +1,5 @@
"""
-Provides functionality inspired by the Refined-Power Analysis attack by Goubin.
-
- A Refined Power-Analysis Attack on Elliptic Curve Cryptosystems, Louis Goubin, PKC '03
- `<https://dl.acm.org/doi/10.5555/648120.747060>`_
+Provides functionality inspired by the Refined-Power Analysis attack by Goubin [RPA]_.
"""
from public import public
from typing import MutableMapping, Optional, Callable, List
diff --git a/pyecsca/sca/re/zvp.py b/pyecsca/sca/re/zvp.py
index 53a38a2..7e3d790 100644
--- a/pyecsca/sca/re/zvp.py
+++ b/pyecsca/sca/re/zvp.py
@@ -1,8 +1,5 @@
"""
-Provides functionality inspired by the Zero-value point attack.
-
- Zero-Value Point Attacks on Elliptic Curve Cryptosystem, Toru Akishita & Tsuyoshi Takagi , ISC '03
- `<https://doi.org/10.1007/10958513_17>`_
+Provides functionality inspired by the Zero-value point attack [ZVP]_.
Implements ZVP point construction from [FFD]_.
"""