aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile6
-rw-r--r--README.md2
-rw-r--r--docs/conf.py1
-rw-r--r--docs/index.rst9
-rw-r--r--pyecsca/ec/configuration.py15
-rw-r--r--pyecsca/ec/context.py25
-rw-r--r--pyecsca/ec/coordinates.py4
-rw-r--r--pyecsca/ec/curve.py23
-rw-r--r--pyecsca/ec/error.py37
-rw-r--r--pyecsca/ec/formula.py50
-rw-r--r--pyecsca/ec/key_agreement.py8
-rw-r--r--pyecsca/ec/key_generation.py17
-rw-r--r--pyecsca/ec/mod.py10
-rw-r--r--pyecsca/ec/model.py14
-rw-r--r--pyecsca/ec/mult.py51
-rw-r--r--pyecsca/ec/naf.py4
-rw-r--r--pyecsca/ec/op.py10
-rw-r--r--pyecsca/ec/params.py10
-rw-r--r--pyecsca/ec/point.py11
-rw-r--r--pyecsca/ec/signature.py27
-rw-r--r--pyecsca/ec/transformations.py4
-rw-r--r--pyecsca/misc/cfg.py63
-rw-r--r--pyecsca/sca/re/rpa.py4
-rw-r--r--pyecsca/sca/scope/base.py13
-rw-r--r--pyecsca/sca/scope/chipwhisperer.py4
-rw-r--r--pyecsca/sca/scope/picoscope_alt.py11
-rw-r--r--pyecsca/sca/scope/picoscope_sdk.py27
-rw-r--r--pyecsca/sca/target/ISO7816.py16
-rw-r--r--pyecsca/sca/target/PCSC.py6
-rw-r--r--pyecsca/sca/target/base.py4
-rw-r--r--pyecsca/sca/target/binary.py6
-rw-r--r--pyecsca/sca/target/chipwhisperer.py6
-rw-r--r--pyecsca/sca/target/ectester.py9
-rw-r--r--pyecsca/sca/target/flash.py4
-rw-r--r--pyecsca/sca/target/serial.py12
-rw-r--r--pyecsca/sca/target/simpleserial.py14
-rw-r--r--pyecsca/sca/trace/align.py98
-rw-r--r--pyecsca/sca/trace/combine.py18
-rw-r--r--pyecsca/sca/trace/edit.py4
-rw-r--r--pyecsca/sca/trace/filter.py16
-rw-r--r--pyecsca/sca/trace/match.py23
-rw-r--r--pyecsca/sca/trace/plot.py4
-rw-r--r--pyecsca/sca/trace/process.py24
-rw-r--r--pyecsca/sca/trace/sampling.py13
-rw-r--r--pyecsca/sca/trace/test.py13
-rw-r--r--pyecsca/sca/trace/trace.py16
-rw-r--r--pyecsca/sca/trace_set/base.py6
-rw-r--r--pyecsca/sca/trace_set/hdf5.py7
-rw-r--r--pyecsca/sca/trace_set/inspector.py6
-rw-r--r--pyecsca/sca/trace_set/pickle.py5
50 files changed, 386 insertions, 404 deletions
diff --git a/Makefile b/Makefile
index 326e80e..bee57d7 100644
--- a/Makefile
+++ b/Makefile
@@ -30,6 +30,9 @@ codestyle:
codestyle-all:
flake8 --extend-ignore=E501,F405,F403,F401,E126,E203 pyecsca test
+docstyle:
+ pydocstyle pyecsca --ignore=D1,D203,D212 -e --count
+
black:
black pyecsca
@@ -58,6 +61,7 @@ help:
@echo " - typecheck-all: Use mypy to verify the use of types in pyecsca and in tests."
@echo " - codestyle: Use flake8 to check codestyle in pyecsca."
@echo " - codestyle-all: Use flake8 to check codestyle in pyecsca and in tests."
+ @echo " - docstyle: Use pydocstyle to check format of docstrings."
@echo " - black: Run black on pyecsca sources (will transform them inplace)."
@echo " - black-all: Run black on pyecsca sources and tests (will transform them inplace)."
@echo " - perf: Run performance measurements (prints results and stores them in .perf/)."
@@ -65,4 +69,4 @@ help:
@echo " - docs: Build docs using sphinx."
@echo " - help: Show this help."
-.PHONY: test test-plots test-all typecheck typecheck-all codestyle codestyle-all black black-all perf doc-coverage docs
+.PHONY: test test-plots test-all typecheck typecheck-all codestyle codestyle-all docstyle black black-all perf doc-coverage docs
diff --git a/README.md b/README.md
index 3cd7584..b2a9b05 100644
--- a/README.md
+++ b/README.md
@@ -61,6 +61,7 @@ It also supports working with [Riscure](https://www.riscure.com) Inspector trace
### Testing & Development
See the [Makefile](Makefile) for tests, performance measurement, codestyle and type checking commands.
+Use [black](https://github.com/psf/black) for code-formatting.
- [nose2](https://nose2.readthedocs.io)
- [green](https://github.com/CleanCut/green)
@@ -78,6 +79,7 @@ See the [Makefile](Makefile) for tests, performance measurement, codestyle and t
- [sphinx](https://www.sphinx-doc.org/)
- [sphinx-autodoc-typehints](https://pypi.org/project/sphinx-autodoc-typehints/)
- [nbsphinx](https://nbsphinx.readthedocs.io/)
+ - [sphinx-paramlinks](https://pypi.org/project/sphinx-paramlinks/)
## License
diff --git a/docs/conf.py b/docs/conf.py
index 8fafb3b..f8499fe 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -46,6 +46,7 @@ extensions = [
'sphinx.ext.todo',
'sphinx.ext.mathjax',
'sphinx.ext.viewcode',
+ 'sphinx_paramlinks',
'nbsphinx'
]
diff --git a/docs/index.rst b/docs/index.rst
index bde29be..bb95c4a 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -98,8 +98,11 @@ Requirements
It also supports working with Riscure_ Inspector trace sets, which are of a proprietary format.
-Testing
--------
+Testing & Development
+---------------------
+
+See the Makefile for tests, performance measurement, codestyle and type checking commands.
+Use black_ for code-formatting.
- nose2_
- green_
@@ -118,6 +121,7 @@ Docs
- sphinx_
- sphinx-autodoc-typehints_
- nbsphinx_
+ - sphinx-paramlinks_
License
=======
@@ -178,5 +182,6 @@ this support is very appreciated.
.. _sphinx: https://www.sphinx-doc.org/
.. _sphinx-autodoc-typehints: https://pypi.org/project/sphinx-autodoc-typehints/
.. _nbsphinx: https://nbsphinx.readthedocs.io/
+.. _sphinx-paramlinks: https://pypi.org/project/sphinx-paramlinks/
.. _Explicit-Formulas Database: https://www.hyperelliptic.org/EFD/index.html
.. _Riscure: https://www.riscure.com/
diff --git a/pyecsca/ec/configuration.py b/pyecsca/ec/configuration.py
index d92486d..e14b182 100644
--- a/pyecsca/ec/configuration.py
+++ b/pyecsca/ec/configuration.py
@@ -1,6 +1,4 @@
-"""
-This module provides a way to work with and enumerate implementation configurations.
-"""
+"""This module provides a way to work with and enumerate implementation configurations."""
from dataclasses import dataclass
from enum import Enum
from itertools import product
@@ -117,13 +115,14 @@ class Configuration(object):
def all_configurations(**kwargs) -> Generator[Configuration, Configuration, None]:
"""
Get all implementation configurations that match the given `kwargs`.
- The keys in `kwargs` should be some of the attributes in the :py:class:`Configuration`,
+
+ The keys in :paramref:`~.all_configurations.kwargs` should be some of the attributes in the :py:class:`Configuration`,
and the values limit the returned configurations to configuration matching them.
.. note::
- The `formulas` attribute is unsupported and formulas should be provided using the `scalarmult`
- attribute, which is either a subclass of the :py:class:`ScalarMultiplier` class or an instance
- of it or a dictionary giving arguments to a constructor of some :py:class:`ScalarMultiplier`
+ The ``formulas`` attribute is unsupported and formulas should be provided using the ``scalarmult``
+ attribute, which is either a subclass of the :py:class:`~.mult.ScalarMultiplier` class or an instance
+ of it or a dictionary giving arguments to a constructor of some :py:class:`~.mult.ScalarMultiplier`
subclass.
.. warning::
@@ -131,7 +130,7 @@ def all_configurations(**kwargs) -> Generator[Configuration, Configuration, None
memory space.
:param kwargs: The configuration parameters to match.
- :return: A generator of the configurations
+ :return: A generator of the configurations.
"""
def is_optional(arg_type):
diff --git a/pyecsca/ec/context.py b/pyecsca/ec/context.py
index a5d6633..0313f4b 100644
--- a/pyecsca/ec/context.py
+++ b/pyecsca/ec/context.py
@@ -1,7 +1,8 @@
"""
-This module provides classes for tracing the execution of operations (key generation, scalar multiplication, formula
-execution, operation evaluation). These operations are traced in `Context` classes using `Actions`. Different contexts
-trace actions differently.
+This module provides classes for tracing the execution of operations.
+
+The operations include key generation, scalar multiplication, formula execution and individual operation evaluation.
+These operations are traced in `Context` classes using `Actions`. Different contexts trace actions differently.
A :py:class:`DefaultContext` traces actions into a tree as they are executed (a scalar
multiplication actions has as its children an ordered list of the individual formula executions it has done).
@@ -95,8 +96,9 @@ class Tree(OrderedDict):
def get_by_index(self, path: List[int]) -> Tuple[Any, Any]:
"""
- Get the key and value in the tree at a position given by the path of indices
- (the nodes inside a level of a tree are ordered by insertion order).
+ Get the key and value in the tree at a position given by the path of indices.
+
+ The nodes inside a level of a tree are ordered by insertion order.
:param path: The path to get.
:return: The key and value.
@@ -134,8 +136,11 @@ class Tree(OrderedDict):
@public
class Context(ABC):
- """A context is an object that traces actions which happen. There is always one
- context active, see functions :py:func:`getcontext`, :py:func:`setcontext` and :py:func:`resetcontext`.
+ """
+ Context is an object that traces actions which happen.
+
+ There is always one context active, see functions :py:func:`getcontext`,
+ :py:func:`setcontext` and :py:func:`resetcontext`.
"""
@abstractmethod
@@ -162,7 +167,7 @@ class Context(ABC):
@public
class NullContext(Context):
- """A context that does not trace any actions."""
+ """Context that does not trace any actions."""
def enter_action(self, action: Action) -> None:
pass
@@ -173,7 +178,7 @@ class NullContext(Context):
@public
class DefaultContext(Context):
- """A context that traces executions of actions in a tree."""
+ """Context that traces executions of actions in a tree."""
actions: Tree
current: List[Action]
@@ -197,7 +202,7 @@ class DefaultContext(Context):
@public
class PathContext(Context):
- """A context that traces targeted actions."""
+ """Context that traces targeted actions."""
path: List[int]
current: List[int]
diff --git a/pyecsca/ec/coordinates.py b/pyecsca/ec/coordinates.py
index 24a0b1a..1797576 100644
--- a/pyecsca/ec/coordinates.py
+++ b/pyecsca/ec/coordinates.py
@@ -1,6 +1,4 @@
-"""
-This module provides a coordinate model class.
-"""
+"""This module provides a coordinate model class."""
from ast import parse, Module
from os.path import join
from typing import List, Any, MutableMapping
diff --git a/pyecsca/ec/curve.py b/pyecsca/ec/curve.py
index bfa58c8..1140a04 100644
--- a/pyecsca/ec/curve.py
+++ b/pyecsca/ec/curve.py
@@ -1,6 +1,4 @@
-"""
-This module provides an elliptic curve class.
-"""
+"""This module provides an elliptic curve class."""
from ast import Module
from copy import copy
from typing import MutableMapping, Union, List, Optional
@@ -15,7 +13,7 @@ from .point import Point, InfinityPoint
@public
class EllipticCurve(object):
- """An elliptic curve."""
+ """Elliptic curve."""
model: CurveModel
"""The model of the curve."""
@@ -82,6 +80,7 @@ class EllipticCurve(object):
def affine_add(self, one: Point, other: Point) -> Point:
"""
Add two affine points using the affine addition formula.
+
Handles the case of point at infinity gracefully (short-circuits).
:param one: One point.
@@ -99,6 +98,7 @@ class EllipticCurve(object):
def affine_double(self, one: Point) -> Point:
"""
Double an affine point using the affine doubling formula.
+
Handles the case of point at infinity gracefully (short-circuits).
:param one: A point.
@@ -111,6 +111,7 @@ class EllipticCurve(object):
def affine_negate(self, one: Point) -> Point:
"""
Negate an affine point using the affine negation formula.
+
Handles the case of point at infinity gracefully (short-circuits).
:param one: A point.
@@ -123,6 +124,7 @@ class EllipticCurve(object):
def affine_multiply(self, point: Point, scalar: int) -> Point:
"""
Multiply an affine point by a scalar using the affine doubling and addition formulas.
+
Handles the case of point at infinity gracefully (short-circuits).
:param point: The point to multiply.
@@ -147,9 +149,9 @@ class EllipticCurve(object):
@property
def affine_neutral(self) -> Optional[Point]:
"""
- Get the neutral point in affine form, if it has one, otherwise `None`.
+ Get the neutral point in affine form, if it has one, otherwise ``None``.
- :return: The affine neutral point or `None`.
+ :return: The affine neutral point or ``None``.
"""
if not self.neutral_is_affine:
return None
@@ -168,7 +170,8 @@ class EllipticCurve(object):
return bool(self.model.base_neutral)
def is_neutral(self, point: Point) -> bool:
- """Check whether the point is the neutral point.
+ """
+ Check whether the point is the neutral point.
:param point: The point to test.
:return: Whether it is the neutral point.
@@ -203,8 +206,10 @@ class EllipticCurve(object):
def decode_point(self, encoded: bytes) -> Point:
"""
- Decode a point encoded as a sequence of bytes (ANSI X9.62). This decoding is the same as ANSI X9.63 for
- the affine coordinate system and for others it only implements the uncompressed variant.
+ Decode a point encoded as a sequence of bytes (ANSI X9.62).
+
+ This decoding is the same as ANSI X9.63 for the affine coordinate system and for others it
+ only implements the uncompressed variant.
.. warning::
The point is not validated to be on the curve (if the uncompressed encoding is used).
diff --git a/pyecsca/ec/error.py b/pyecsca/ec/error.py
index 26e7945..fac4aac 100644
--- a/pyecsca/ec/error.py
+++ b/pyecsca/ec/error.py
@@ -1,23 +1,28 @@
-"""
-This module contains exceptions and warnings used in the library.
-"""
+"""This module contains exceptions and warnings used in the library."""
from public import public
from ..misc.cfg import getconfig
@public
class NonInvertibleError(ArithmeticError):
+ """Non-invertible element was inverted."""
+
pass
@public
class NonInvertibleWarning(UserWarning):
+ """Non-invertible element was inverted."""
+
pass
def raise_non_invertible():
- """Raise either :py:class:`NonInvertibleError` or :py:class:`NonInvertiblerWarning` or ignore.
- Depends on the current config value of `no_inverse_action`."""
+ """
+ Raise either :py:class:`NonInvertibleError` or :py:class:`NonInvertiblerWarning` or ignore.
+
+ Depends on the current config value of :py:attr:`Config.ec.no_inverse_action`.
+ """
cfg = getconfig()
if cfg.ec.no_inverse_action == "error":
raise NonInvertibleError("Element not invertible.")
@@ -27,17 +32,24 @@ def raise_non_invertible():
@public
class NonResidueError(ArithmeticError):
+ """Non-residue element was square-rooted."""
+
pass
@public
class NonResidueWarning(UserWarning):
+ """Non-residue element was square-rooted."""
+
pass
def raise_non_residue():
- """Raise either :py:class:`NonResidueError` or :py:class:`NonResidueWarning` or ignore.
- Depends on the current config value of `non_residue_action`."""
+ """
+ Raise either :py:class:`NonResidueError` or :py:class:`NonResidueWarning` or ignore.
+
+ Depends on the current config value of :py:attr:`Config.ec.non_residue_action`.
+ """
cfg = getconfig()
if cfg.ec.non_residue_action == "error":
raise NonResidueError("No square root exists.")
@@ -47,17 +59,24 @@ def raise_non_residue():
@public
class UnsatisfiedAssumptionError(ValueError):
+ """Unsatisfied assumption was hit."""
+
pass
@public
class UnsatisfiedAssumptionWarning(UserWarning):
+ """Unsatisfied assumption was hit."""
+
pass
def raise_unsatisified_assumption(action: str, msg: str):
- """Raise either :py:class:`UnsatisfiedAssumptionError` or :py:class:`UnsatisfiedAssumptionWarning` or ignore.
- Depends on the value of `action`."""
+ """
+ Raise either :py:class:`UnsatisfiedAssumptionError` or :py:class:`UnsatisfiedAssumptionWarning` or ignore.
+
+ Depends on the value of :paramref:`~.raise_unsatisified_assumption.action`.
+ """
if action == "error":
raise UnsatisfiedAssumptionError(msg)
elif action == "warning":
diff --git a/pyecsca/ec/formula.py b/pyecsca/ec/formula.py
index e0ce886..efe87c0 100644
--- a/pyecsca/ec/formula.py
+++ b/pyecsca/ec/formula.py
@@ -1,6 +1,4 @@
-"""
-This module provides an abstract base class of a formula along with concrete instantiations.
-"""
+"""This module provides an abstract base class of a formula along with concrete instantiations."""
from abc import ABC, abstractmethod
from ast import parse, Expression
from astunparse import unparse
@@ -20,7 +18,7 @@ from ..misc.cfg import getconfig
@public
class OpResult(object):
- """A result of an operation."""
+ """Result of an operation."""
parents: Tuple
op: OpType
@@ -44,7 +42,7 @@ class OpResult(object):
@public
class FormulaAction(ResultAction):
- """An execution of a formula, on some input points and parameters, with some outputs."""
+ """Execution of a formula, on some input points and parameters, with some outputs."""
formula: "Formula"
"""The formula that was executed."""
@@ -96,7 +94,7 @@ class FormulaAction(ResultAction):
@public
class Formula(ABC):
- """A formula operating on points."""
+ """Formula operating on points."""
name: str
"""Name of the formula."""
@@ -247,67 +245,67 @@ class Formula(ABC):
@property
@abstractmethod
def input_index(self):
- """The starting index where this formula reads its inputs."""
+ """Return the starting index where this formula reads its inputs."""
raise NotImplementedError
@property
@abstractmethod
def output_index(self) -> int:
- """The starting index where this formula stores its outputs."""
+ """Return the starting index where this formula stores its outputs."""
raise NotImplementedError
@property
@abstractmethod
def inputs(self) -> Set[str]:
- """The input variables of the formula."""
+ """Return the input variables of the formula."""
raise NotImplementedError
@property
@abstractmethod
def outputs(self) -> Set[str]:
- """The output variables of the formula."""
+ """Return the output variables of the formula."""
raise NotImplementedError
@property
def num_operations(self) -> int:
- """Number of operations."""
+ """Return the number of operations."""
return len(list(filter(lambda op: op.operator is not None, self.code)))
@property
def num_multiplications(self) -> int:
- """Number of multiplications."""
+ """Return the number of multiplications."""
return len(list(filter(lambda op: op.operator == OpType.Mult, self.code)))
@property
def num_divisions(self) -> int:
- """Number of divisions."""
+ """Return the number of divisions."""
return len(list(filter(lambda op: op.operator == OpType.Div, self.code)))
@property
def num_inversions(self) -> int:
- """Number of inversions."""
+ """Return the number of inversions."""
return len(list(filter(lambda op: op.operator == OpType.Inv, self.code)))
@property
def num_powers(self) -> int:
- """Number of powers."""
+ """Return the number of powers."""
return len(list(filter(lambda op: op.operator == OpType.Pow, self.code)))
@property
def num_squarings(self) -> int:
- """Number of squarings."""
+ """Return the number of squarings."""
return len(list(filter(lambda op: op.operator == OpType.Sqr, self.code)))
@property
def num_addsubs(self) -> int:
- """Number of additions and subtractions."""
+ """Return the number of additions and subtractions."""
return len(
list(filter(lambda op: op.operator in (OpType.Add, OpType.Sub), self.code))
)
class EFDFormula(Formula):
- """A formula from the `Explicit-Formulas Database <https://www.hyperelliptic.org/EFD/>`_."""
+ """Formula from the `Explicit-Formulas Database <https://www.hyperelliptic.org/EFD/>`_."""
def __init__(self, path: str, name: str, coordinate_model: Any):
self.name = name
@@ -386,7 +384,7 @@ class EFDFormula(Formula):
@public
class AdditionFormula(Formula, ABC):
- """A formula that adds two points."""
+ """Formula that adds two points."""
shortname = "add"
num_inputs = 2
@@ -400,7 +398,7 @@ class AdditionEFDFormula(AdditionFormula, EFDFormula):
@public
class DoublingFormula(Formula, ABC):
- """A formula that doubles a point."""
+ """Formula that doubles a point."""
shortname = "dbl"
num_inputs = 1
@@ -414,7 +412,7 @@ class DoublingEFDFormula(DoublingFormula, EFDFormula):
@public
class TriplingFormula(Formula, ABC):
- """A formula that triples a point."""
+ """Formula that triples a point."""
shortname = "tpl"
num_inputs = 1
@@ -428,7 +426,7 @@ class TriplingEFDFormula(TriplingFormula, EFDFormula):
@public
class NegationFormula(Formula, ABC):
- """A formula that negates a point."""
+ """Formula that negates a point."""
shortname = "neg"
num_inputs = 1
@@ -442,7 +440,7 @@ class NegationEFDFormula(NegationFormula, EFDFormula):
@public
class ScalingFormula(Formula, ABC):
- """A formula that somehow scales the point (to a given representative of a projective class)."""
+ """Formula that somehow scales the point (to a given representative of a projective class)."""
shortname = "scl"
num_inputs = 1
@@ -457,7 +455,8 @@ class ScalingEFDFormula(ScalingFormula, EFDFormula):
@public
class DifferentialAdditionFormula(Formula, ABC):
"""
- A differential addition formula that adds two points with a known difference.
+ Differential addition formula that adds two points with a known difference.
+
The first input point is the difference of the third input and the second input (`P[0] = P[2] - P[1]`).
"""
@@ -474,7 +473,8 @@ class DifferentialAdditionEFDFormula(DifferentialAdditionFormula, EFDFormula):
@public
class LadderFormula(Formula, ABC):
"""
- A ladder formula for simultaneous addition of two points and doubling of the one of them, with a known difference.
+ Ladder formula for simultaneous addition of two points and doubling of the one of them, with a known difference.
+
The first input point is the difference of the third input and the second input (`P[0] = P[2] - P[1]`).
The first output point is the doubling of the second input point (`O[0] = 2 * P[1]`).
The second output point is the addition of the second and third input points (`O[1] = P[1] + P[2]`).
diff --git a/pyecsca/ec/key_agreement.py b/pyecsca/ec/key_agreement.py
index b58374f..207923e 100644
--- a/pyecsca/ec/key_agreement.py
+++ b/pyecsca/ec/key_agreement.py
@@ -1,6 +1,4 @@
-"""
-This module provides an implementation of ECDH (Elliptic Curve Diffie-Hellman).
-"""
+"""This module provides an implementation of ECDH (Elliptic Curve Diffie-Hellman)."""
import hashlib
from typing import Optional, Any
@@ -15,7 +13,7 @@ from .point import Point
@public
class ECDHAction(ResultAction):
- """An ECDH key exchange."""
+ """ECDH key exchange."""
params: DomainParameters
hash_algo: Optional[Any]
@@ -41,7 +39,7 @@ class ECDHAction(ResultAction):
@public
class KeyAgreement(object):
- """An EC based key agreement primitive. (ECDH)"""
+ """EC based key agreement primitive (ECDH)."""
mult: ScalarMultiplier
params: DomainParameters
diff --git a/pyecsca/ec/key_generation.py b/pyecsca/ec/key_generation.py
index a08c767..c379a81 100644
--- a/pyecsca/ec/key_generation.py
+++ b/pyecsca/ec/key_generation.py
@@ -1,6 +1,4 @@
-"""
-This module provides a key generator for elliptic curve keypairs.
-"""
+"""This module provides a key generator for elliptic curve keypairs."""
from typing import Tuple
from public import public
@@ -28,7 +26,13 @@ class KeygenAction(ResultAction):
@public
class KeyGeneration(object):
- """Key generator."""
+ """
+ Key generator.
+
+ :param mult: The scalar multiplier to use during key generation.
+ :param params: The domain parameters over which to generate the keypair.
+ :param affine: Whether to transform the public key point to the affine form during key generation.
+ """
mult: ScalarMultiplier
params: DomainParameters
@@ -37,11 +41,6 @@ class KeyGeneration(object):
def __init__(
self, mult: ScalarMultiplier, params: DomainParameters, affine: bool = False
):
- """
- :param mult: The scalar multiplier to use during key generation.
- :param params: The domain parameters over which to generate the keypair.
- :param affine: Whether to transform the public key point to the affine form during key generation.
- """
self.mult = mult
self.params = params
self.mult.init(self.params, self.params.generator)
diff --git a/pyecsca/ec/mod.py b/pyecsca/ec/mod.py
index 4712de1..b5f71f4 100644
--- a/pyecsca/ec/mod.py
+++ b/pyecsca/ec/mod.py
@@ -1,5 +1,7 @@
"""
-This module provides several implementations of an element of ℤₙ. The base class :py:class:`Mod` dynamically
+This module provides several implementations of an element of ℤₙ.
+
+The base class :py:class:`Mod` dynamically
dispatches to the implementation chosen by the runtime configuration of the library
(see :py:class:`pyecsca.misc.cfg.Config`). A Python integer based implementation is available under
:py:class:`RawMod`. A symbolic implementation based on sympy is available under :py:class:`SymbolicMod`. If
@@ -599,7 +601,6 @@ if has_gmp:
return GMPMod(res, self.n)
def is_residue(self) -> bool:
- """Whether this element is a quadratic residue (only implemented for prime modulus)."""
if not _is_prime(self.n):
raise NotImplementedError
if self.x == 0:
@@ -609,11 +610,6 @@ if has_gmp:
return gmpy2.legendre(self.x, self.n) == 1
def sqrt(self) -> "GMPMod":
- """
- The modular square root of this element (only implemented for prime modulus).
-
- Uses the `Tonelli-Shanks <https://en.wikipedia.org/wiki/Tonelli–Shanks_algorithm>`_ algorithm.
- """
if not _is_prime(self.n):
raise NotImplementedError
if self.x == 0:
diff --git a/pyecsca/ec/model.py b/pyecsca/ec/model.py
index 861dfca..f0cba1a 100644
--- a/pyecsca/ec/model.py
+++ b/pyecsca/ec/model.py
@@ -1,6 +1,4 @@
-"""
-This module provides curve model classes for the supported curve models.
-"""
+"""This module provides curve model classes for the supported curve models."""
from ast import parse, Expression, Module
from os.path import join
from typing import List, MutableMapping
@@ -13,7 +11,7 @@ from .coordinates import EFDCoordinateModel, CoordinateModel
@public
class CurveModel(object):
- """A model(form) of an elliptic curve."""
+ """Model(form) of an elliptic curve."""
name: str
shortname: str
@@ -115,7 +113,7 @@ class EFDCurveModel(CurveModel):
@public
class ShortWeierstrassModel(EFDCurveModel):
"""
- A short-Weierstrass curve model, with the equation:
+ Short-Weierstrass curve model.
.. math::
@@ -129,7 +127,7 @@ class ShortWeierstrassModel(EFDCurveModel):
@public
class MontgomeryModel(EFDCurveModel):
"""
- A Montgomery curve model, with the equation:
+ Montgomery curve model.
.. math::
@@ -143,7 +141,7 @@ class MontgomeryModel(EFDCurveModel):
@public
class EdwardsModel(EFDCurveModel):
"""
- An Edwards curve model, with the equation:
+ Edwards curve model.
.. math::
@@ -157,7 +155,7 @@ class EdwardsModel(EFDCurveModel):
@public
class TwistedEdwardsModel(EFDCurveModel):
"""
- A twisted-Edwards curve model, with the equation:
+ Twisted-Edwards curve model.
.. math::
diff --git a/pyecsca/ec/mult.py b/pyecsca/ec/mult.py
index 5b1d83e..6588055 100644
--- a/pyecsca/ec/mult.py
+++ b/pyecsca/ec/mult.py
@@ -1,6 +1,4 @@
-"""
-This module provides several classes implementing different scalar multiplication algorithms.
-"""
+"""This module provides several classes implementing different scalar multiplication algorithms."""
from abc import ABC, abstractmethod
from copy import copy
from typing import Mapping, Tuple, Optional, MutableMapping, ClassVar, Set, Type
@@ -154,7 +152,7 @@ class ScalarMultiplier(ABC):
def init(self, params: DomainParameters, point: Point):
"""
- Initialize the scalar multiplier with params and a point.
+ Initialize the scalar multiplier with :paramref:`~.init.params` and a :paramref:`~.init.point`.
.. warning::
The point is not verified to be on the curve represented in the domain parameters.
@@ -191,7 +189,7 @@ class LTRMultiplier(ScalarMultiplier):
"""
Classic double and add scalar multiplication algorithm, that scans the scalar left-to-right (msb to lsb).
- The `always` parameter determines whether the double and add always method is used.
+ :param always: Whether the double and add always method is used.
"""
requires = {AdditionFormula, DoublingFormula}
@@ -214,7 +212,7 @@ class LTRMultiplier(ScalarMultiplier):
def multiply(self, scalar: int) -> Point:
if not self._initialized:
- raise ValueError("ScalaMultiplier not initialized.")
+ raise ValueError("ScalarMultiplier not initialized.")
with ScalarMultiplicationAction(self._point, scalar) as action:
if scalar == 0:
return action.exit(copy(self._params.curve.neutral))
@@ -243,7 +241,7 @@ class RTLMultiplier(ScalarMultiplier):
"""
Classic double and add scalar multiplication algorithm, that scans the scalar right-to-left (lsb to msb).
- The `always` parameter determines whether the double and add always method is used.
+ :param always: Whether the double and add always method is used.
"""
requires = {AdditionFormula, DoublingFormula}
@@ -263,7 +261,7 @@ class RTLMultiplier(ScalarMultiplier):
def multiply(self, scalar: int) -> Point:
if not self._initialized:
- raise ValueError("ScalaMultiplier not initialized.")
+ raise ValueError("ScalarMultiplier not initialized.")
with ScalarMultiplicationAction(self._point, scalar) as action:
if scalar == 0:
return action.exit(copy(self._params.curve.neutral))
@@ -285,9 +283,10 @@ class RTLMultiplier(ScalarMultiplier):
@public
class CoronMultiplier(ScalarMultiplier):
"""
- Coron's double and add resistant against SPA, from:
+ Coron's double and add resistant against SPA.
- Resistance against Differential Power Analysis for Elliptic Curve Cryptosystems
+ From:
+ **Resistance against Differential Power Analysis for Elliptic Curve Cryptosystems**
https://link.springer.com/content/pdf/10.1007/3-540-48059-5_25.pdf
"""
@@ -306,7 +305,7 @@ class CoronMultiplier(ScalarMultiplier):
def multiply(self, scalar: int) -> Point:
if not self._initialized:
- raise ValueError("ScalaMultiplier not initialized.")
+ raise ValueError("ScalarMultiplier not initialized.")
with ScalarMultiplicationAction(self._point, scalar) as action:
if scalar == 0:
return action.exit(copy(self._params.curve.neutral))
@@ -324,9 +323,7 @@ class CoronMultiplier(ScalarMultiplier):
@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."""
requires = {LadderFormula}
optionals = {DoublingFormula, ScalingFormula}
@@ -347,7 +344,7 @@ class LadderMultiplier(ScalarMultiplier):
def multiply(self, scalar: int) -> Point:
if not self._initialized:
- raise ValueError("ScalaMultiplier not initialized.")
+ raise ValueError("ScalarMultiplier not initialized.")
with ScalarMultiplicationAction(self._point, scalar) as action:
if scalar == 0:
return action.exit(copy(self._params.curve.neutral))
@@ -372,9 +369,7 @@ class LadderMultiplier(ScalarMultiplier):
@public
class SimpleLadderMultiplier(ScalarMultiplier):
- """
- Montgomery ladder multiplier, using addition and doubling formulas.
- """
+ """Montgomery ladder multiplier, using addition and doubling formulas."""
requires = {AdditionFormula, DoublingFormula}
optionals = {ScalingFormula}
@@ -393,7 +388,7 @@ class SimpleLadderMultiplier(ScalarMultiplier):
def multiply(self, scalar: int) -> Point:
if not self._initialized:
- raise ValueError("ScalaMultiplier not initialized.")
+ raise ValueError("ScalarMultiplier not initialized.")
with ScalarMultiplicationAction(self._point, scalar) as action:
if scalar == 0:
return action.exit(copy(self._params.curve.neutral))
@@ -417,9 +412,7 @@ 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."""
requires = {DifferentialAdditionFormula, DoublingFormula}
optionals = {ScalingFormula}
@@ -438,7 +431,7 @@ class DifferentialLadderMultiplier(ScalarMultiplier):
def multiply(self, scalar: int) -> Point:
if not self._initialized:
- raise ValueError("ScalaMultiplier not initialized.")
+ raise ValueError("ScalarMultiplier not initialized.")
with ScalarMultiplicationAction(self._point, scalar) as action:
if scalar == 0:
return action.exit(copy(self._params.curve.neutral))
@@ -463,9 +456,7 @@ class DifferentialLadderMultiplier(ScalarMultiplier):
@public
class BinaryNAFMultiplier(ScalarMultiplier):
- """
- Binary NAF (Non Adjacent Form) multiplier, left-to-right.
- """
+ """Binary NAF (Non Adjacent Form) multiplier, left-to-right."""
requires = {AdditionFormula, DoublingFormula, NegationFormula}
optionals = {ScalingFormula}
@@ -490,7 +481,7 @@ class BinaryNAFMultiplier(ScalarMultiplier):
def multiply(self, scalar: int) -> Point:
if not self._initialized:
- raise ValueError("ScalaMultiplier not initialized.")
+ raise ValueError("ScalarMultiplier not initialized.")
with ScalarMultiplicationAction(self._point, scalar) as action:
if scalar == 0:
return action.exit(copy(self._params.curve.neutral))
@@ -509,9 +500,7 @@ class BinaryNAFMultiplier(ScalarMultiplier):
@public
class WindowNAFMultiplier(ScalarMultiplier):
- """
- Window NAF (Non Adjacent Form) multiplier, left-to-right.
- """
+ """Window NAF (Non Adjacent Form) multiplier, left-to-right."""
requires = {AdditionFormula, DoublingFormula, NegationFormula}
optionals = {ScalingFormula}
@@ -551,7 +540,7 @@ class WindowNAFMultiplier(ScalarMultiplier):
def multiply(self, scalar: int) -> Point:
if not self._initialized:
- raise ValueError("ScalaMultiplier not initialized.")
+ raise ValueError("ScalarMultiplier not initialized.")
with ScalarMultiplicationAction(self._point, scalar) as action:
if scalar == 0:
return action.exit(copy(self._params.curve.neutral))
diff --git a/pyecsca/ec/naf.py b/pyecsca/ec/naf.py
index 9d01555..2595a4e 100644
--- a/pyecsca/ec/naf.py
+++ b/pyecsca/ec/naf.py
@@ -1,6 +1,4 @@
-"""
-This module provides functions for computing the Non-Adjacent Form (NAF) of integers.
-"""
+"""This module provides functions for computing the Non-Adjacent Form (NAF) of integers."""
from public import public
from typing import List
diff --git a/pyecsca/ec/op.py b/pyecsca/ec/op.py
index e500914..bdfe553 100644
--- a/pyecsca/ec/op.py
+++ b/pyecsca/ec/op.py
@@ -1,6 +1,4 @@
-"""
-This module provides a class for a code operation.
-"""
+"""This module provides a class for a code operation."""
from ast import (
Module,
walk,
@@ -29,7 +27,7 @@ from .mod import Mod
@public
class OpType(Enum):
- """A type of binary and unary operators."""
+ """Type of binary and unary operators."""
Add = (2, "+")
Sub = (2, "-")
@@ -48,7 +46,7 @@ class OpType(Enum):
@public
class CodeOp(object):
- """An operation that can be executed."""
+ """Operation that can be executed."""
result: str
"""The result variable of the operation (e.g. the `r` in `r = 2*a`)."""
@@ -135,6 +133,6 @@ class CodeOp(object):
return f"CodeOp({self.result} = f(params={self.parameters}, vars={self.variables}, consts={self.constants}))"
def __call__(self, *args, **kwargs: Mod) -> Mod:
- """Execute this operation with kwargs."""
+ """Execute this operation with :paramref:`.__call__.kwargs`."""
exec(self.compiled, {}, kwargs)
return kwargs[self.result]
diff --git a/pyecsca/ec/params.py b/pyecsca/ec/params.py
index 196f7b0..81888c0 100644
--- a/pyecsca/ec/params.py
+++ b/pyecsca/ec/params.py
@@ -1,6 +1,7 @@
"""
-This module provides functions for obtaining domain parameters from the `std-curves <https://github.com/J08nY/std-curves>`_
-repository. It also provides a domain parameter class and a class for a whole category of domain parameters.
+This module provides functions for obtaining domain parameters from the `std-curves <https://github.com/J08nY/std-curves>`_ repository.
+
+It also provides a domain parameter class and a class for a whole category of domain parameters.
"""
import json
from sympy import Poly, FF, symbols, sympify
@@ -319,8 +320,9 @@ def get_params(
category: str, name: str, coords: str, infty: bool = True
) -> DomainParameters:
"""
- Retrieve a curve from a set of stored parameters. Uses the std-curves database at
- https://github.com/J08nY/std-curves.
+ Retrieve a curve from a set of stored parameters.
+
+ Uses the std-curves database at https://github.com/J08nY/std-curves.
:param category: The category of the curve.
:param name: The name of the curve.
diff --git a/pyecsca/ec/point.py b/pyecsca/ec/point.py
index 1d074fe..a509db2 100644
--- a/pyecsca/ec/point.py
+++ b/pyecsca/ec/point.py
@@ -1,6 +1,4 @@
-"""
-This module provides a `Point` class and a special `InfinityPoint` class for the point at infinity.
-"""
+"""This module provides a :py:class:`.Point` class and a special :py:class:`.InfinityPoint` class for the point at infinity."""
from copy import copy
from typing import Mapping, TYPE_CHECKING
@@ -151,7 +149,7 @@ class Point(object):
return action.exit(Point(coordinate_model, **result))
def equals_affine(self, other: "Point") -> bool:
- """Test whether this point is equal to `other` irrespective of the coordinate model (in the affine sense)."""
+ """Test whether this point is equal to :paramref:`~.equals_affine.other` irrespective of the coordinate model (in the affine sense)."""
if not isinstance(other, Point) or isinstance(other, InfinityPoint):
return False
if self.coordinate_model.curve_model != other.coordinate_model.curve_model:
@@ -160,8 +158,9 @@ class Point(object):
def equals_scaled(self, other: "Point") -> bool:
"""
- Test whether this point is equal to `other` using the "z" scaling formula,
- which maps the projective class to a single representative.
+ Test whether this point is equal to :paramref:`~.equals_scaled.other` using the "z" scaling formula.
+
+ The "z" scalig formula maps the projective class to a single representative.
:param other: The point to compare
:raises ValueError: If the "z" formula is not available for the coordinate system.
diff --git a/pyecsca/ec/signature.py b/pyecsca/ec/signature.py
index 0e6f546..d85d58f 100644
--- a/pyecsca/ec/signature.py
+++ b/pyecsca/ec/signature.py
@@ -1,6 +1,4 @@
-"""
-This module provides an implementation of ECDSA (Elliptic Curve Digital Signature Algorithm).
-"""
+"""This module provides an implementation of ECDSA (Elliptic Curve Digital Signature Algorithm)."""
import hashlib
from typing import Optional, Any
@@ -17,21 +15,12 @@ from .point import Point
@public
class SignatureResult(object):
- """An ECDSA signature result (r, s)."""
+ """ECDSA signature result (r, s)."""
r: int
s: int
- def __init__(
- self,
- r: int,
- s: int,
- data: Optional[bytes] = None,
- digest: Optional[bytes] = None,
- nonce: Optional[int] = None,
- privkey: Optional[Mod] = None,
- pubkey: Optional[Point] = None,
- ):
+ def __init__(self, r: int, s: int):
self.r = r
self.s = s
@@ -80,7 +69,7 @@ class ECDSAAction(Action):
@public
class ECDSASignAction(ECDSAAction):
- """An ECDSA signing."""
+ """ECDSA signing."""
privkey: Mod
@@ -100,7 +89,7 @@ class ECDSASignAction(ECDSAAction):
@public
class ECDSAVerifyAction(ECDSAAction):
- """An ECDSA verification."""
+ """ECDSA verification."""
signature: SignatureResult
pubkey: Point
@@ -123,7 +112,7 @@ class ECDSAVerifyAction(ECDSAAction):
@public
class Signature(object):
- """An EC based signature primitive. (ECDSA)"""
+ """EC based signature primitive (ECDSA)."""
mult: ScalarMultiplier
params: DomainParameters
@@ -180,9 +169,7 @@ class Signature(object):
affine_point = point.to_affine()
r = Mod(int(affine_point.x), self.params.order)
s = nonce.inverse() * (Mod(z, self.params.order) + r * self.privkey)
- return SignatureResult(
- int(r), int(s), digest=digest, nonce=int(nonce), privkey=self.privkey
- )
+ return SignatureResult(int(r), int(s))
def sign_hash(self, digest: bytes, nonce: Optional[int] = None) -> SignatureResult:
"""Sign already hashed data."""
diff --git a/pyecsca/ec/transformations.py b/pyecsca/ec/transformations.py
index 0987235..9d3d650 100644
--- a/pyecsca/ec/transformations.py
+++ b/pyecsca/ec/transformations.py
@@ -1,6 +1,4 @@
-"""
-This module provides functions for transforming curves to different models.
-"""
+"""This module provides functions for transforming curves to different models."""
from public import public
from sympy import FF, symbols, Poly
diff --git a/pyecsca/misc/cfg.py b/pyecsca/misc/cfg.py
index 8a5a9ee..82cb387 100644
--- a/pyecsca/misc/cfg.py
+++ b/pyecsca/misc/cfg.py
@@ -1,6 +1,7 @@
"""
-This module provides functions for runtime configuration of the toolkit, such as how errors are handled, or which
-:py:class:`Mod` implementation is used.
+This module provides functions for runtime configuration of the toolkit.
+
+This includes how errors are handled, or which :py:class:`~pyecsca.ec.mod.Mod` implementation is used.
"""
from copy import deepcopy
from contextvars import ContextVar, Token
@@ -21,11 +22,14 @@ class ECConfig(object):
@property
def no_inverse_action(self) -> str:
"""
- The action to take when a non-invertible element is to be inverted. One of:
+ Return or set the action to take when a non-invertible element is to be inverted.
+
+ One of:
- - `"error"`: Raise :py:class:`pyecsca.ec.error.NonInvertibleError`.
- - `"warning"`: Raise :py:class:`pyecsca.ec.error.NonInvertibleWarning`.
- - `"ignore"`: Ignore the event and compute as if nothing happened."""
+ - ``"error"``: Raise :py:class:`pyecsca.ec.error.NonInvertibleError`.
+ - ``"warning"``: Raise :py:class:`pyecsca.ec.error.NonInvertibleWarning`.
+ - ``"ignore"``: Ignore the event and compute as if nothing happened.
+ """
return self._no_inverse_action
@no_inverse_action.setter
@@ -37,11 +41,14 @@ class ECConfig(object):
@property
def non_residue_action(self) -> str:
"""
- The action to take when a the square-root of a non-residue is to be computed. One of:
+ Return or set the action to take when a the square-root of a non-residue is to be computed.
+
+ One of:
- - `"error"`: Raise :py:class:`pyecsca.ec.error.NonResidueError`.
- - `"warning"`: Raise :py:class:`pyecsca.ec.error.NonResidueWarning`.
- - `"ignore"`: Ignore the event and compute as if nothing happened."""
+ - ``"error"``: Raise :py:class:`pyecsca.ec.error.NonResidueError`.
+ - ``"warning"``: Raise :py:class:`pyecsca.ec.error.NonResidueWarning`.
+ - ``"ignore"``: Ignore the event and compute as if nothing happened.
+ """
return self._non_residue_action
@non_residue_action.setter
@@ -53,15 +60,16 @@ class ECConfig(object):
@property
def unsatisfied_formula_assumption_action(self) -> str:
"""
- The action to take when a formula assumption is unsatisfied during execution.
+ Return or set the action to take when a formula assumption is unsatisfied during execution.
+
This works for assumption that can be ignored without a fatal error,
which are those that are not used to compute a value of an undefined parameter.
- For example, things of the form `Z1 = 1`.
+ For example, things of the form ``Z1 = 1``.
One of:
- - `"error"`: Raise :py:class:`pyecsca.ec.error.UnsatisfiedAssumptionError`.
- - `"warning"`: Raise :py:class:`pyecsca.ec.error.UnsatisfiedAssumptionWarning`.
- - `"ignore"`: Ignore the event and compute as if nothing happened.
+ - ``"error"``: Raise :py:class:`pyecsca.ec.error.UnsatisfiedAssumptionError`.
+ - ``"warning"``: Raise :py:class:`pyecsca.ec.error.UnsatisfiedAssumptionWarning`.
+ - ``"ignore"``: Ignore the event and compute as if nothing happened.
"""
return self._unsatisfied_formula_assumption_action
@@ -74,15 +82,16 @@ class ECConfig(object):
@property
def unsatisfied_coordinate_assumption_action(self) -> str:
"""
- The action to take when a coordinate assumption is unsatisfied during curve creation.
+ Return or set the action to take when a coordinate assumption is unsatisfied during curve creation.
+
This works for assumption that can be ignored without a fatal error,
which are those that are not used to compute a value of an undefined parameter.
- For example, things of the form `a = -1`.
+ For example, things of the form ``a = -1``.
One of:
- - `"error"`: Raise :py:class:`pyecsca.ec.error.UnsatisfiedAssumptionError`.
- - `"warning"`: Raise :py:class:`pyecsca.ec.error.UnsatisfiedAssumptionWarning`.
- - `"ignore"`: Ignore the event and compute as if nothing happened.
+ - ``"error"``: Raise :py:class:`pyecsca.ec.error.UnsatisfiedAssumptionError`.
+ - ``"warning"``: Raise :py:class:`pyecsca.ec.error.UnsatisfiedAssumptionWarning`.
+ - ``"ignore"``: Ignore the event and compute as if nothing happened.
"""
return self._unsatisfied_coordinate_assumption_action
@@ -95,10 +104,12 @@ class ECConfig(object):
@property
def mod_implementation(self) -> str:
"""
- The selected :py:class:`pyecsca.ec.mod.Mod` implementation. One of:
+ Return or set the selected :py:class:`pyecsca.ec.mod.Mod` implementation.
- - `"gmp"`: Requires the GMP library and `gmpy2` package.
- - `"python"`: Doesn't require anything.
+ One of:
+
+ - ``"gmp"``: Requires the GMP library and `gmpy2` package.
+ - ``"python"``: Doesn't require anything.
"""
return self._mod_implementation
@@ -111,7 +122,7 @@ class ECConfig(object):
@public
class Config(object):
- """A runtime configuration for the library."""
+ """Runtime configuration for the library."""
ec: ECConfig
"""Configuration for the :py:mod:`pyecsca.ec` package."""
@@ -157,7 +168,9 @@ def resetconfig(token: Token) -> None:
@public
class TemporaryConfig(object):
"""
- A temporary config context manager, can be entered as follows:
+ Temporary config context manager.
+
+ Can be entered as follows:
.. code-block:: python
diff --git a/pyecsca/sca/re/rpa.py b/pyecsca/sca/re/rpa.py
index b38f389..f21429a 100644
--- a/pyecsca/sca/re/rpa.py
+++ b/pyecsca/sca/re/rpa.py
@@ -1,5 +1,5 @@
"""
-This module provides functionality inspired by the Refined-Power Analysis attack by Goubin:
+This module 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>`_
@@ -23,7 +23,7 @@ from ...ec.context import Context, Action
@public
class MultipleContext(Context):
- """A context that traces the multiples of points computed."""
+ """Context that traces the multiples of points computed."""
base: Optional[Point]
points: MutableMapping[Point, int]
diff --git a/pyecsca/sca/scope/base.py b/pyecsca/sca/scope/base.py
index 595fe32..ced3009 100644
--- a/pyecsca/sca/scope/base.py
+++ b/pyecsca/sca/scope/base.py
@@ -1,6 +1,4 @@
-"""
-This module provides an abstract base class for oscilloscopes.
-"""
+"""This module provides an abstract base class for oscilloscopes."""
from enum import Enum, auto
from typing import Tuple, Sequence, Optional
@@ -34,9 +32,11 @@ class Scope(object):
self, frequency: int, pretrig: int, posttrig: int
) -> Tuple[int, int]:
"""
- Setup the frequency and sample count for the measurement. The scope might not support
- the requested values and will adjust them to get the next best frequency and the largest
- supported number of samples (or the number of samples requested).
+ Setup the frequency and sample count for the measurement.
+
+ The scope might not support the requested values and will adjust them to get
+ the next best frequency and the largest supported number of samples
+ (or the number of samples requested).
:param frequency: The requested frequency in Hz.
:param pretrig: The requested number of samples to measure before the trigger.
@@ -70,6 +70,7 @@ class Scope(object):
) -> None:
"""
Setup a trigger on a particular `channel`, the channel has to be set up and enabled.
+
The trigger will fire based on the `threshold` and `direction`, if enabled, the trigger
will capture after `delay` ticks pass. If trigger conditions do not hold it will fire
automatically after `timeout` milliseconds.
diff --git a/pyecsca/sca/scope/chipwhisperer.py b/pyecsca/sca/scope/chipwhisperer.py
index 05c8e69..f1c8d94 100644
--- a/pyecsca/sca/scope/chipwhisperer.py
+++ b/pyecsca/sca/scope/chipwhisperer.py
@@ -1,6 +1,4 @@
-"""
-This module provides an oscilloscope class using the ChipWhisperer-Lite scope.
-"""
+"""This module provides an oscilloscope class using the ChipWhisperer-Lite scope."""
from typing import Optional, Tuple, Sequence, Set
import numpy as np
diff --git a/pyecsca/sca/scope/picoscope_alt.py b/pyecsca/sca/scope/picoscope_alt.py
index 577c7dc..e8f17a5 100644
--- a/pyecsca/sca/scope/picoscope_alt.py
+++ b/pyecsca/sca/scope/picoscope_alt.py
@@ -1,7 +1,4 @@
-"""
-This module provides an oscilloscope class for the PicoScope branded oscilloscopes using
-the alternative `pico-python <https://github.com/colinoflynn/pico-python>`_ bindings.
-"""
+"""This module provides an oscilloscope class for the PicoScope branded oscilloscopes using the alternative `pico-python <https://github.com/colinoflynn/pico-python>`_ bindings."""
from time import time_ns, sleep
import numpy as np
from typing import Optional, Tuple, Sequence, Union
@@ -18,7 +15,11 @@ from ..trace import Trace
@public
class PicoScopeAlt(Scope): # pragma: no cover
- """A PicoScope based scope. Supports series 3000,4000,5000 and 6000."""
+ """
+ PicoScope based scope.
+
+ Supports series 3000,4000,5000 and 6000.
+ """
def __init__(self, ps: Union[PS3000, PS4000, PS5000, PS6000]):
"""
diff --git a/pyecsca/sca/scope/picoscope_sdk.py b/pyecsca/sca/scope/picoscope_sdk.py
index d94c0fe..3ee54cf 100644
--- a/pyecsca/sca/scope/picoscope_sdk.py
+++ b/pyecsca/sca/scope/picoscope_sdk.py
@@ -1,7 +1,4 @@
-"""
-This module provides an oscilloscope class for PicoScope branded oscilloscopes using
-the official `picosdk-python-wrappers <https://github.com/picotech/picosdk-python-wrappers>`_.
-"""
+"""This module provides an oscilloscope class for PicoScope branded oscilloscopes using the official `picosdk-python-wrappers <https://github.com/picotech/picosdk-python-wrappers>`_."""
import ctypes
from math import log2, floor
from time import time_ns, sleep
@@ -46,7 +43,7 @@ def adc2volt(
:param adc: Either a single value (:py:class:`ctypes.c_int16`) or an array (:py:class:`np.ndarray`) of those to convert.
:param volt_range: The voltage range used for collecting the samples.
:param adc_minmax:
- :param dtype: The numpy `dtype` of the output.
+ :param dtype: The numpy ``dtype`` of the output.
:return: The converted values.
"""
if isinstance(adc, ctypes.c_int16):
@@ -65,7 +62,7 @@ def volt2adc(
:param volt: Either a single value (:py:class:`float`) or an array (:py:class:`np.ndarray`) of those to convert.
:param volt_range: The voltage range used for collecting the samples.
:param adc_minmax:
- :param dtype: The numpy `dtype` of the output.
+ :param dtype: The numpy ``dtype`` of the output.
:return: The converted values.
"""
if isinstance(volt, float):
@@ -77,7 +74,7 @@ def volt2adc(
@public
class PicoScopeSdk(Scope): # pragma: no cover
- """A PicoScope based scope."""
+ """PicoScope based scope."""
MODULE: Library
PREFIX: str
@@ -348,7 +345,7 @@ if isinstance(ps3000, CannotFindPicoSDKError):
@public
class PS3000Scope(PicoScopeSdk): # pragma: no cover
- """A PicoScope 3000 series oscilloscope is not available. (Install `libps3000`)."""
+ """PicoScope 3000 series oscilloscope is not available (Install `libps3000`)."""
def __init__(self, variant: Optional[str] = None):
super().__init__(variant)
@@ -359,7 +356,7 @@ else: # pragma: no cover
@public
class PS3000Scope(PicoScopeSdk): # type: ignore
- """A PicoScope 3000 series oscilloscope."""
+ """PicoScope 3000 series oscilloscope."""
MODULE = ps3000
PREFIX = "ps3000"
@@ -415,7 +412,7 @@ if isinstance(ps4000, CannotFindPicoSDKError):
@public
class PS4000Scope(PicoScopeSdk): # pragma: no cover
- """A PicoScope 4000 series oscilloscope is not available. (Install `libps4000`)."""
+ """PicoScope 4000 series oscilloscope is not available (Install `libps4000`)."""
def __init__(self, variant: Optional[str] = None):
super().__init__(variant)
@@ -426,7 +423,7 @@ else: # pragma: no cover
@public
class PS4000Scope(PicoScopeSdk): # type: ignore
- """A PicoScope 4000 series oscilloscope."""
+ """PicoScope 4000 series oscilloscope."""
MODULE = ps4000
PREFIX = "ps4000"
@@ -478,7 +475,7 @@ if isinstance(ps5000, CannotFindPicoSDKError):
@public
class PS5000Scope(PicoScopeSdk): # pragma: no cover
- """A PicoScope 5000 series oscilloscope is not available. (Install `libps5000`)."""
+ """PicoScope 5000 series oscilloscope is not available (Install `libps5000`)."""
def __init__(self, variant: Optional[str] = None):
super().__init__(variant)
@@ -489,7 +486,7 @@ else: # pragma: no cover
@public
class PS5000Scope(PicoScopeSdk): # type: ignore
- """A PicoScope 5000 series oscilloscope."""
+ """PicoScope 5000 series oscilloscope."""
MODULE = ps5000
PREFIX = "ps5000"
@@ -530,7 +527,7 @@ if isinstance(ps6000, CannotFindPicoSDKError):
@public
class PS6000Scope(PicoScopeSdk): # pragma: no cover
- """A PicoScope 6000 series oscilloscope is not available. (Install `libps6000`)."""
+ """PicoScope 6000 series oscilloscope is not available (Install `libps6000`)."""
def __init__(self, variant: Optional[str] = None):
super().__init__(variant)
@@ -541,7 +538,7 @@ else: # pragma: no cover
@public
class PS6000Scope(PicoScopeSdk): # type: ignore
- """A PicoScope 6000 series oscilloscope."""
+ """PicoScope 6000 series oscilloscope."""
MODULE = ps6000
PREFIX = "ps6000"
diff --git a/pyecsca/sca/target/ISO7816.py b/pyecsca/sca/target/ISO7816.py
index 896cb62..8da06f8 100644
--- a/pyecsca/sca/target/ISO7816.py
+++ b/pyecsca/sca/target/ISO7816.py
@@ -1,6 +1,4 @@
-"""
-This module provides classes for working with ISO7816-4 APDUs and an abstract base class for an ISO7816-4 based target.
-"""
+"""This module provides classes for working with ISO7816-4 APDUs and an abstract base class for an ISO7816-4 based target."""
from abc import abstractmethod, ABC
from dataclasses import dataclass
from typing import Optional
@@ -13,7 +11,7 @@ from .base import Target
@public
@dataclass
class CommandAPDU(object): # pragma: no cover
- """A command APDU that can be sent to an ISO7816-4 target."""
+ """Command APDU that can be sent to an ISO7816-4 target."""
cls: int
ins: int
@@ -82,7 +80,7 @@ class CommandAPDU(object): # pragma: no cover
@public
@dataclass
class ResponseAPDU(object):
- """A response APDU that can be received from an ISO7816-4 target."""
+ """Response APDU that can be received from an ISO7816-4 target."""
data: bytes
sw: int
@@ -90,18 +88,18 @@ class ResponseAPDU(object):
@public
class ISO7816Target(Target, ABC):
- """An ISO7816-4 target."""
+ """ISO7816-4 target."""
@property
@abstractmethod
def atr(self) -> bytes:
- """The ATR (Answer To Reset) of the target."""
+ """Return the ATR (Answer To Reset) of the target."""
raise NotImplementedError
@abstractmethod
def select(self, aid: bytes) -> bool:
"""
- Select an applet with `aid`.
+ Select an applet with :paramref:`~.select.aid`.
:param aid: The AID of the applet to select.
:return: Whether the selection was successful.
@@ -121,7 +119,7 @@ class ISO7816Target(Target, ABC):
@public
class ISO7816:
- """A bunch of ISO7816-4 constants (status words)."""
+ """Bunch of ISO7816-4 constants (status words)."""
SW_FILE_FULL = 0x6A84
SW_UNKNOWN = 0x6F00
diff --git a/pyecsca/sca/target/PCSC.py b/pyecsca/sca/target/PCSC.py
index 4b2620c..ca77d12 100644
--- a/pyecsca/sca/target/PCSC.py
+++ b/pyecsca/sca/target/PCSC.py
@@ -1,6 +1,4 @@
-"""
-This module provides a smartcard target communicating via PC/SC (Personal Computer/Smart Card).
-"""
+"""This module provides a smartcard target communicating via PC/SC (Personal Computer/Smart Card)."""
from typing import Union
from public import public
@@ -14,7 +12,7 @@ from .ISO7816 import ISO7816Target, CommandAPDU, ResponseAPDU, ISO7816
@public
class PCSCTarget(ISO7816Target): # pragma: no cover
- """A smartcard target communicating via PCSC."""
+ """Smartcard target communicating via PCSC."""
def __init__(self, reader: Union[str, PCSCReader]):
if isinstance(reader, str):
diff --git a/pyecsca/sca/target/base.py b/pyecsca/sca/target/base.py
index 6e1758d..0c24f8d 100644
--- a/pyecsca/sca/target/base.py
+++ b/pyecsca/sca/target/base.py
@@ -1,6 +1,4 @@
-"""
-This module provides an abstract base class for targets.
-"""
+"""This module provides an abstract base class for targets."""
from abc import ABC, abstractmethod
from public import public
diff --git a/pyecsca/sca/target/binary.py b/pyecsca/sca/target/binary.py
index d4c402e..291f8bc 100644
--- a/pyecsca/sca/target/binary.py
+++ b/pyecsca/sca/target/binary.py
@@ -1,6 +1,4 @@
-"""
-This module provides a binary target class which represents a target that is a runnable binary on the host.
-"""
+"""This module provides a binary target class which represents a target that is a runnable binary on the host."""
import subprocess
from subprocess import Popen
from typing import Optional, Union, List
@@ -12,7 +10,7 @@ from .serial import SerialTarget
@public
class BinaryTarget(SerialTarget):
- """A binary target that is runnable on the host and communicates using the stdin/stdout streams."""
+ """Binary target that is runnable on the host and communicates using the stdin/stdout streams."""
binary: List[str]
process: Optional[Popen] = None
diff --git a/pyecsca/sca/target/chipwhisperer.py b/pyecsca/sca/target/chipwhisperer.py
index 4037f12..c9380cc 100644
--- a/pyecsca/sca/target/chipwhisperer.py
+++ b/pyecsca/sca/target/chipwhisperer.py
@@ -1,5 +1,6 @@
"""
This module provides a `ChipWhisperer <https://github.com/newaetech/chipwhisperer/>`_ target class.
+
ChipWhisperer is a side-channel analysis tool and framework. A ChipWhisperer target is one
that uses the ChipWhisperer's SimpleSerial communication protocol and is communicated with
using ChipWhisperer-Lite or Pro.
@@ -17,10 +18,7 @@ from .simpleserial import SimpleSerialTarget
@public
class ChipWhispererTarget(Flashable, SimpleSerialTarget): # pragma: no cover
- """
- A ChipWhisperer-based target, using the SimpleSerial protocol and communicating
- using ChipWhisperer-Lite/Pro.
- """
+ """ChipWhisperer-based target, using the SimpleSerial protocol and communicating using ChipWhisperer-Lite/Pro."""
def __init__(
self, target: SimpleSerial, scope: ScopeTemplate, programmer, **kwargs
diff --git a/pyecsca/sca/target/ectester.py b/pyecsca/sca/target/ectester.py
index 08c0e02..4057e61 100644
--- a/pyecsca/sca/target/ectester.py
+++ b/pyecsca/sca/target/ectester.py
@@ -1,6 +1,4 @@
-"""
-This module provides an `ECTester <https://github.com/crocs-muni/ECTester/>`_ target class.
-"""
+"""This module provides an `ECTester <https://github.com/crocs-muni/ECTester/>`_ target class."""
from abc import ABC
from binascii import hexlify
from enum import IntEnum, IntFlag
@@ -511,10 +509,7 @@ class InfoResponse(Response): # pragma: no cover
@public
class ECTesterTarget(PCSCTarget): # pragma: no cover
- """
- A smartcard target which communicates with the `ECTester <https://github.com/crocs-muni/ECTester>`_
- applet on smartcards of the JavaCard platform using PCSC.
- """
+ """Smartcard target which communicates with the `ECTester <https://github.com/crocs-muni/ECTester>`_ sapplet on smartcards of the JavaCard platform using PCSC."""
CLA_ECTESTER = 0xB0
AID_PREFIX = bytes([0x45, 0x43, 0x54, 0x65, 0x73, 0x74, 0x65, 0x72])
diff --git a/pyecsca/sca/target/flash.py b/pyecsca/sca/target/flash.py
index cc70a9c..b644c27 100644
--- a/pyecsca/sca/target/flash.py
+++ b/pyecsca/sca/target/flash.py
@@ -1,6 +1,4 @@
-"""
-This module provides a mix-in class of a flashable target (e.g. one where the code gets flashed to it before running).
-"""
+"""This module provides a mix-in class of a flashable target (e.g. one where the code gets flashed to it before running)."""
from public import public
from abc import ABC, abstractmethod
diff --git a/pyecsca/sca/target/serial.py b/pyecsca/sca/target/serial.py
index a435ca0..fba5d69 100644
--- a/pyecsca/sca/target/serial.py
+++ b/pyecsca/sca/target/serial.py
@@ -1,6 +1,4 @@
-"""
-This module provides an abstract serial target, that communicates by writing and reading a stream of bytes.
-"""
+"""This module provides an abstract serial target, that communicates by writing and reading a stream of bytes."""
from abc import abstractmethod
from public import public
@@ -10,12 +8,12 @@ from .base import Target
@public
class SerialTarget(Target):
- """A serial target."""
+ """Serial target."""
@abstractmethod
def write(self, data: bytes) -> None:
"""
- Write the `data` bytes to the target's serial input.
+ Write the :paramref:`~.write.data` bytes to the target's serial input.
:param data: The data to write.
"""
@@ -24,9 +22,9 @@ class SerialTarget(Target):
@abstractmethod
def read(self, num: int = 0, timeout: int = 0) -> bytes:
"""
- Read upto `num` bytes or until `timeout` milliseconds from the target's serial output.
+ Read upto :paramref:`~.read.num` bytes or until :paramref:`~.read.timeout` milliseconds from the target's serial output.
- :param num: The number of bytes to read, `0` for all available.
+ :param num: The number of bytes to read, ``0`` for all available.
:param timeout: The timeout in milliseconds.
:return: The bytes read.
"""
diff --git a/pyecsca/sca/target/simpleserial.py b/pyecsca/sca/target/simpleserial.py
index 03cb9cf..a28672e 100644
--- a/pyecsca/sca/target/simpleserial.py
+++ b/pyecsca/sca/target/simpleserial.py
@@ -1,7 +1,4 @@
-"""
-This module provides an abstract target class communicating using the
-`ChipWhisperer's <https://github.com/newaetech/chipwhisperer/>`_ SimpleSerial protocol.
-"""
+"""This module provides an abstract target class communicating using the `ChipWhisperer's <https://github.com/newaetech/chipwhisperer/>`_ SimpleSerial protocol."""
from time import time_ns, sleep
from typing import Mapping, Union
@@ -42,6 +39,12 @@ class SimpleSerialTarget(SerialTarget):
"""A SimpleSerial target, sends and receives SimpleSerial messages over a serial link."""
def recv_msgs(self, timeout: int) -> Mapping[str, SimpleSerialMessage]:
+ """
+ Receive :py:class:`SimpleSerialMessage` messages, while waiting upto :paramref:`~.recv_msgs.timeout` seconds.
+
+ :param timeout: How long to wait.
+ :return: The received messages with their char.
+ """
start = time_ns() // 1000000
buffer = bytes()
while not buffer.endswith(b"z00\n"):
@@ -65,8 +68,7 @@ class SimpleSerialTarget(SerialTarget):
self, cmd: SimpleSerialMessage, timeout: int
) -> Mapping[str, SimpleSerialMessage]:
"""
- Send a :py:class:`SimpleSerialMessage` and receive the response messages that the command produces,
- within a `timeout`.
+ Send a :py:class:`SimpleSerialMessage` and receive the response messages that the command produces, within a :paramref:`~.send_cmd.timeout`.
:param cmd: The command message to send.
:param timeout: The timeout value to wait for the responses.
diff --git a/pyecsca/sca/trace/align.py b/pyecsca/sca/trace/align.py
index 070ec2f..ac30fe5 100644
--- a/pyecsca/sca/trace/align.py
+++ b/pyecsca/sca/trace/align.py
@@ -1,6 +1,4 @@
-"""
-This module provides functions for aligning traces in a trace set to a reference trace within it.
-"""
+"""This module provides functions for aligning traces in a trace set to a reference trace within it."""
import numpy as np
from copy import deepcopy
from fastdtw import fastdtw, dtw
@@ -44,19 +42,21 @@ def align_correlation(
min_correlation: float = 0.5
) -> Tuple[List[Trace], List[int]]:
"""
- Align `traces` to the reference `trace`. Using the cross-correlation of a part of the reference
+ Align :paramref:`~.align_correlation.traces` to the :paramref:`~.align_correlation.reference` trace using correlation.
+
+ Use the cross-correlation of a part of the reference
trace starting at `reference_offset` with `reference_length` and try to match it to a part of
the trace being matched that is at most `max_offset` mis-aligned from the reference, pick the
alignment offset with the largest cross-correlation. If the maximum cross-correlation of the
trace parts being matched is below `min_correlation`, do not include the trace.
- :param reference:
- :param traces:
- :param reference_offset:
- :param reference_length:
- :param max_offset:
- :param min_correlation:
- :return:
+ :param reference: Trace to align to.
+ :param traces: Traces to align.
+ :param reference_offset: Offset into the reference trace to start the aligning from.
+ :param reference_length: Length of the part of the reference trace to align.
+ :param max_offset: Maximum offset to try to align the traces by.
+ :param min_correlation: Minimal correlation between the aligned trace and the reference trace.
+ :return: A tuple of: the list of the aligned traces (with the reference) and offsets used in alignment.
"""
reference_centered = normalize(reference)
reference_part = reference_centered.samples[
@@ -95,16 +95,18 @@ def align_peaks(
max_offset: int
) -> Tuple[List[Trace], List[int]]:
"""
- Align `traces` to the reference `trace` so that the maximum value within the reference trace
+ Align :paramref:`~.align_correlation.traces` to the :paramref:`~.align_correlation.reference` trace using peaks.
+
+ Align so that the maximum value within the reference trace
window from `reference_offset` of `reference_length` aligns with the maximum
value of the trace being aligned within `max_offset` of the reference window.
- :param reference:
- :param traces:
- :param reference_offset:
- :param reference_length:
- :param max_offset:
- :return:
+ :param reference: Trace to align to.
+ :param traces: Traces to align.
+ :param reference_offset: Offset into the reference trace to start the aligning from.
+ :param reference_length: Length of the part of the reference trace to align.
+ :param max_offset: Maximum offset to try to align the traces by.
+ :return: A tuple of: the list of the aligned traces (with the reference) and offsets used in alignment.
"""
reference_part = reference.samples[
reference_offset : reference_offset + reference_length
@@ -134,17 +136,20 @@ def align_offset(
max_dist: float = float("inf")
) -> Tuple[List[Trace], List[int]]:
"""
- Align `traces` to the reference `trace` so that the value of the `dist_func` is minimized
+ Align :paramref:`~.align_correlation.traces` to the :paramref:`~.align_correlation.reference` trace using a distance function.
+
+ Align so that the value of the `dist_func` is minimized
between the reference trace window from `reference_offset` of `reference_length` and the trace
being aligned within `max_offset` of the reference window.
- :param reference:
- :param traces:
- :param reference_offset:
- :param reference_length:
- :param max_offset:
- :param dist_func:
- :return:
+ :param reference: Trace to align to.
+ :param traces: Traces to align.
+ :param reference_offset: Offset into the reference trace to start the aligning from.
+ :param reference_length: Length of the part of the reference trace to align.
+ :param max_offset: Maximum offset to try to align the traces by.
+ :param dist_func: Distance function to use.
+ :param max_dist: Maximum distance between the aligned trace and the reference trace.
+ :return: A tuple of: the list of the aligned traces (with the reference) and offsets used in alignment.
"""
reference_part = reference.samples[
reference_offset : reference_offset + reference_length
@@ -181,16 +186,18 @@ def align_sad(
max_offset: int
) -> Tuple[List[Trace], List[int]]:
"""
- Align `traces` to the reference `trace` so that the Sum Of Absolute Differences between the
+ Align :paramref:`~.align_correlation.traces` to the :paramref:`~.align_correlation.reference` trace using Sum of Absolute Differences.
+
+ Align so that the Sum Of Absolute Differences between the
reference trace window from `reference_offset` of `reference_length` and the trace being aligned
within `max_offset` of the reference window is maximized.
- :param reference:
- :param traces:
- :param reference_offset:
- :param reference_length:
- :param max_offset:
- :return:
+ :param reference: Trace to align to.
+ :param traces: Traces to align.
+ :param reference_offset: Offset into the reference trace to start the aligning from.
+ :param reference_length: Length of the part of the reference trace to align.
+ :param max_offset: Maximum offset to try to align the traces by.
+ :return: A tuple of: the list of the aligned traces (with the reference) and offsets used in alignment.
"""
def sad(reference_part, trace_part):
@@ -211,19 +218,20 @@ def align_dtw_scale(
reference: Trace, *traces: Trace, radius: int = 1, fast: bool = True
) -> List[Trace]:
"""
- Align `traces` to the `reference` trace.
- Using fastdtw (Dynamic Time Warping) with scaling as per:
+ Align :paramref:`~.align_correlation.traces` to the :paramref:`~.align_correlation.reference` trace.
+
+ Use fastdtw (Dynamic Time Warping) with scaling as per:
Jasper G. J. van Woudenberg, Marc F. Witteman, Bram Bakker:
- Improving Differential Power Analysis by Elastic Alignment
+ **Improving Differential Power Analysis by Elastic Alignment**
https://pdfs.semanticscholar.org/aceb/7c307098a414d7c384d6189226e4375cf02d.pdf
- :param reference:
- :param traces:
+ :param reference: Trace to align to.
+ :param traces: Traces to align.
:param radius:
:param fast:
- :return:
+ :return: List of the aligned traces (with the reference).
"""
result = [deepcopy(reference)]
reference_samples = reference.samples
@@ -249,18 +257,20 @@ def align_dtw(
reference: Trace, *traces: Trace, radius: int = 1, fast: bool = True
) -> List[Trace]:
"""
- Align `traces` to the `reference` trace. Using fastdtw (Dynamic Time Warping) as per:
+ Align `traces` to the `reference` trace.
+
+ Use fastdtw (Dynamic Time Warping) as per:
Stan Salvador, Philip Chan:
- FastDTW: Toward Accurate Dynamic Time Warping in Linear Time and Space
+ **FastDTW: Toward Accurate Dynamic Time Warping in Linear Time and Space**
https://cs.fit.edu/~pkc/papers/tdm04.pdf
- :param reference:
- :param traces:
+ :param reference: Trace to align to.
+ :param traces: Traces to align.
:param radius:
:param fast:
- :return:
+ :return: List of the aligned traces (with the reference).
"""
result = [deepcopy(reference)]
reference_samples = reference.samples
diff --git a/pyecsca/sca/trace/combine.py b/pyecsca/sca/trace/combine.py
index 853b127..9df6059 100644
--- a/pyecsca/sca/trace/combine.py
+++ b/pyecsca/sca/trace/combine.py
@@ -1,6 +1,4 @@
-"""
-This module provides functions for combining traces sample-wise.
-"""
+"""This module provides functions for combining traces sample-wise."""
from typing import Callable, Optional, Tuple
import numpy as np
@@ -12,7 +10,7 @@ from .trace import Trace, CombinedTrace
@public
def average(*traces: Trace) -> Optional[CombinedTrace]:
"""
- Average `traces`, sample-wise.
+ Average :paramref:`~.average.traces`, sample-wise.
:param traces:
:return:
@@ -35,7 +33,7 @@ def conditional_average(
*traces: Trace, condition: Callable[[Trace], bool]
) -> Optional[CombinedTrace]:
"""
- Average `traces` for which the `condition` is True, sample-wise.
+ Average :paramref:`~.conditional_average.traces` for which the :paramref:`~.conditional_average.condition` is ``True``, sample-wise.
:param traces:
:param condition:
@@ -47,7 +45,7 @@ def conditional_average(
@public
def standard_deviation(*traces: Trace) -> Optional[CombinedTrace]:
"""
- Compute the sample standard-deviation of the `traces`, sample-wise.
+ Compute the sample standard-deviation of the :paramref:`~.standard_deviation.traces`, sample-wise.
:param traces:
:return:
@@ -74,7 +72,7 @@ def standard_deviation(*traces: Trace) -> Optional[CombinedTrace]:
@public
def variance(*traces: Trace) -> Optional[CombinedTrace]:
"""
- Compute the sample variance of the `traces`, sample-wise.
+ Compute the sample variance of the :paramref:`~.variance.traces`, sample-wise.
:param traces:
:return:
@@ -101,7 +99,7 @@ def variance(*traces: Trace) -> Optional[CombinedTrace]:
@public
def average_and_variance(*traces) -> Optional[Tuple[CombinedTrace, CombinedTrace]]:
"""
- Compute the average and sample variance of the `traces`, sample-wise.
+ Compute the average and sample variance of the :paramref:`~.average_and_variance.traces`, sample-wise.
:param traces:
:return:
@@ -130,7 +128,7 @@ def average_and_variance(*traces) -> Optional[Tuple[CombinedTrace, CombinedTrace
@public
def add(*traces: Trace) -> Optional[CombinedTrace]:
"""
- Add `traces`, sample-wise.
+ Add :paramref:`~.add.traces`, sample-wise.
:param traces:
:return:
@@ -149,7 +147,7 @@ def add(*traces: Trace) -> Optional[CombinedTrace]:
@public
def subtract(one: Trace, other: Trace) -> CombinedTrace:
"""
- Subtract `other` from `one`, sample-wise.
+ Subtract :paramref:`~.subtract.other` from :paramref:`~.subtract.one`, sample-wise.
:param one:
:param other:
diff --git a/pyecsca/sca/trace/edit.py b/pyecsca/sca/trace/edit.py
index 5aec15f..83653d0 100644
--- a/pyecsca/sca/trace/edit.py
+++ b/pyecsca/sca/trace/edit.py
@@ -1,6 +1,4 @@
-"""
-This module provides functions for editing traces as if they were tapes you can trim, reverse, etc.
-"""
+"""This module provides functions for editing traces as if they were tapes you can trim, reverse, etc."""
import numpy as np
from public import public
from typing import Union, Tuple, Any
diff --git a/pyecsca/sca/trace/filter.py b/pyecsca/sca/trace/filter.py
index 2fbc284..48d065c 100644
--- a/pyecsca/sca/trace/filter.py
+++ b/pyecsca/sca/trace/filter.py
@@ -1,6 +1,4 @@
-"""
-This module provides functions for filtering traces using digital (low/high/band)-pass and bandstop filters.
-"""
+"""This module provides functions for filtering traces using digital (low/high/band)-pass and bandstop filters."""
from public import public
from scipy.signal import butter, lfilter
from typing import Union, Tuple
@@ -31,8 +29,7 @@ def _filter_any(
@public
def filter_lowpass(trace: Trace, sampling_frequency: int, cutoff: int) -> Trace:
"""
- Apply a lowpass digital filter (Butterworth) to `trace`, given `sampling_frequency` and
- `cutoff` frequency.
+ Apply a lowpass digital filter (Butterworth) to `trace`, given `sampling_frequency` and `cutoff` frequency.
:param trace:
:param sampling_frequency:
@@ -45,8 +42,7 @@ def filter_lowpass(trace: Trace, sampling_frequency: int, cutoff: int) -> Trace:
@public
def filter_highpass(trace: Trace, sampling_frequency: int, cutoff: int) -> Trace:
"""
- Apply a highpass digital filter (Butterworth) to `trace`, given `sampling_frequency` and
- `cutoff` frequency.
+ Apply a highpass digital filter (Butterworth) to `trace`, given `sampling_frequency` and `cutoff` frequency.
:param trace:
:param sampling_frequency:
@@ -61,8 +57,7 @@ def filter_bandpass(
trace: Trace, sampling_frequency: int, low: int, high: int
) -> Trace:
"""
- Apply a bandpass digital filter (Butterworth) to `trace`, given `sampling_frequency`, with the
- passband from `low` to `high`.
+ Apply a bandpass digital filter (Butterworth) to `trace`, given `sampling_frequency`, with the passband from `low` to `high`.
:param trace:
:param sampling_frequency:
@@ -78,8 +73,7 @@ def filter_bandstop(
trace: Trace, sampling_frequency: int, low: int, high: int
) -> Trace:
"""
- Apply a bandstop digital filter (Butterworth) to `trace`, given `sampling_frequency`, with the
- stopband from `low` to `high`.
+ Apply a bandstop digital filter (Butterworth) to `trace`, given `sampling_frequency`, with the stopband from `low` to `high`.
:param trace:
:param sampling_frequency:
diff --git a/pyecsca/sca/trace/match.py b/pyecsca/sca/trace/match.py
index e1181e9..bcd4334 100644
--- a/pyecsca/sca/trace/match.py
+++ b/pyecsca/sca/trace/match.py
@@ -1,6 +1,4 @@
-"""
-This module provides functions for matching a pattern within a trace to it.
-"""
+"""This module provides functions for matching a pattern within a trace to it."""
import numpy as np
from scipy.signal import find_peaks
from public import public
@@ -14,12 +12,15 @@ from .trace import Trace
@public
def match_pattern(trace: Trace, pattern: Trace, threshold: float = 0.8) -> List[int]:
"""
- Match a `pattern` to a `trace`. Returns indices where the pattern matches, e.g. those where correlation
- of the two traces has peaks larger than `threshold`. Uses the :py:func:`scipy.signal.find_peaks` function.
+ Match a :paramref:`~.match_pattern.pattern` to a :paramref:`~.match_pattern.trace`.
+
+ Return the indices where the pattern matches, e.g. those where correlation
+ of the two traces has peaks larger than :paramref:`~.match_pattern.threshold`.
+ Uses the :py:func:`scipy.signal.find_peaks` function.
:param trace: The trace to match into.
:param pattern: The pattern to match.
- :param threshold: The threshold passed to :py:func:`scipy.signal.find_peaks` as a `prominence` value.
+ :param threshold: The threshold passed to :py:func:`scipy.signal.find_peaks` as a ``prominence`` value.
:return: Indices where the pattern matches.
"""
normalized = normalize(trace)
@@ -47,14 +48,16 @@ def match_part(
trace: Trace, offset: int, length: int, threshold: float = 0.8
) -> List[int]:
"""
- Match a part of a `trace` starting at `offset` of `length` to the `trace`. Returns indices where the pattern matches
- , e.g. those where correlation of the two traces has peaks larger than `threshold`. Uses the
- :py:func:`scipy.signal.find_peaks` function.
+ Match a part of a :paramref:`~.match_part.trace` starting at :paramref:`~.match_part.offset` of :paramref:`~.match_part.length` to the :paramref:`~.match_part.trace`.
+
+ Returns indices where the pattern matches, e.g. those where correlation of the two
+ traces has peaks larger than :paramref:`~.match_part.threshold`.
+ Uses the :py:func:`scipy.signal.find_peaks` function.
:param trace: The trace to match into.
:param offset: The start of the pattern in the trace to match.
:param length: The length of the pattern in the trace to match.
- :param threshold: The threshold passed to :py:func:`scipy.signal.find_peaks` as a `prominence` value.
+ :param threshold: The threshold passed to :py:func:`scipy.signal.find_peaks` as a ``prominence`` value.
:return: Indices where the part of the trace matches matches.
"""
return match_pattern(trace, trim(trace, offset, offset + length), threshold)
diff --git a/pyecsca/sca/trace/plot.py b/pyecsca/sca/trace/plot.py
index 6a8cec1..63fa2dd 100644
--- a/pyecsca/sca/trace/plot.py
+++ b/pyecsca/sca/trace/plot.py
@@ -1,6 +1,4 @@
-"""
-This module provides functions for plotting traces.
-"""
+"""This module provides functions for plotting traces."""
from functools import reduce
import holoviews as hv
diff --git a/pyecsca/sca/trace/process.py b/pyecsca/sca/trace/process.py
index 2499df4..dac24ed 100644
--- a/pyecsca/sca/trace/process.py
+++ b/pyecsca/sca/trace/process.py
@@ -1,6 +1,4 @@
-"""
-This module provides functions for sample-wise processing of single traces.
-"""
+"""This module provides functions for sample-wise processing of single traces."""
from typing import cast
import numpy as np
@@ -12,7 +10,7 @@ from .trace import Trace
@public
def absolute(trace: Trace) -> Trace:
"""
- Apply absolute value to samples of `trace`.
+ Apply absolute value to samples of :paramref:`~.absolute.trace`.
:param trace:
:return:
@@ -23,7 +21,7 @@ def absolute(trace: Trace) -> Trace:
@public
def invert(trace: Trace) -> Trace:
"""
- Invert(negate) the samples of `trace`.
+ Invert(negate) the samples of :paramref:`~.invert.trace`.
:param trace:
:return:
@@ -34,7 +32,7 @@ def invert(trace: Trace) -> Trace:
@public
def threshold(trace: Trace, value) -> Trace:
"""
- Map samples of the `trace` to 1 if they are above `value` or to 0.
+ Map samples of the :paramref:`~.threshold.trace` to ``1`` if they are above :paramref:`~.threshold.value` or to ``0``.
:param trace:
:param value:
@@ -55,7 +53,9 @@ def _rolling_window(samples: np.ndarray, window: int) -> np.ndarray:
@public
def rolling_mean(trace: Trace, window: int) -> Trace:
"""
- Compute the rolling mean of `trace` using `window`. Shortens the trace by `window` - 1.
+ Compute the rolling mean of :paramref:`~.rolling_mean.trace` using :paramref:`~.rolling_mean.window`.
+
+ Shortens the trace by ``window - 1``.
:param trace:
:param window:
@@ -74,7 +74,9 @@ def rolling_mean(trace: Trace, window: int) -> Trace:
@public
def offset(trace: Trace, offset) -> Trace:
"""
- Offset samples of `trace` by `offset`, sample-wise (Adds `offset` to all samples).
+ Offset samples of :paramref:`~.offset.trace` by :paramref:`~.offset.offset`, sample-wise.
+
+ Adds :paramref:`~.offset.offset` to all samples.
:param trace:
:param offset:
@@ -90,7 +92,7 @@ def _root_mean_square(trace: Trace):
@public
def recenter(trace: Trace) -> Trace:
"""
- Subtract the root mean square of the `trace` from its samples, sample-wise.
+ Subtract the root mean square of the :paramref:`~.recenter.trace` from its samples, sample-wise.
:param trace:
:return:
@@ -102,7 +104,7 @@ def recenter(trace: Trace) -> Trace:
@public
def normalize(trace: Trace) -> Trace:
"""
- Normalize a `trace` by subtracting its mean and dividing by its standard deviation.
+ Normalize a :paramref:`~.normalize.trace` by subtracting its mean and dividing by its standard deviation.
:param trace:
:return:
@@ -115,7 +117,7 @@ def normalize(trace: Trace) -> Trace:
@public
def normalize_wl(trace: Trace) -> Trace:
"""
- Normalize a `trace` by subtracting its mean and dividing by a multiple (= `len(trace)`) of its standard deviation.
+ Normalize a :paramref:`~.normalize_wl.trace` by subtracting its mean and dividing by a multiple (= ``len(trace)``) of its standard deviation.
:param trace:
:return:
diff --git a/pyecsca/sca/trace/sampling.py b/pyecsca/sca/trace/sampling.py
index 71dbdac..c92a478 100644
--- a/pyecsca/sca/trace/sampling.py
+++ b/pyecsca/sca/trace/sampling.py
@@ -1,6 +1,4 @@
-"""
-This module provides downsampling functions for traces.
-"""
+"""This module provides downsampling functions for traces."""
from typing import cast
import numpy as np
@@ -13,8 +11,7 @@ from .trace import Trace
@public
def downsample_average(trace: Trace, factor: int = 2) -> Trace:
"""
- Downsample samples of `trace` by `factor` by averaging `factor` consecutive samples in
- non-intersecting windows.
+ Downsample samples of `trace` by `factor` by averaging `factor` consecutive samples in non-intersecting windows.
:param trace:
:param factor:
@@ -49,8 +46,7 @@ def downsample_pick(trace: Trace, factor: int = 2, offset: int = 0) -> Trace:
@public
def downsample_max(trace: Trace, factor: int = 2) -> Trace:
"""
- Downsample samples of `trace` by `factor` by taking the maximum out of `factor` consecutive samples in
- non-intersecting windows.
+ Downsample samples of `trace` by `factor` by taking the maximum out of `factor` consecutive samples in non-intersecting windows.
:param trace:
:param factor:
@@ -69,8 +65,7 @@ def downsample_max(trace: Trace, factor: int = 2) -> Trace:
@public
def downsample_min(trace: Trace, factor: int = 2) -> Trace:
"""
- Downsample samples of `trace` by `factor` by taking the minimum out of `factor` consecutive samples in
- non-intersecting windows.
+ Downsample samples of `trace` by `factor` by taking the minimum out of `factor` consecutive samples in non-intersecting windows.
:param trace:
:param factor:
diff --git a/pyecsca/sca/trace/test.py b/pyecsca/sca/trace/test.py
index f90498a..93d7c26 100644
--- a/pyecsca/sca/trace/test.py
+++ b/pyecsca/sca/trace/test.py
@@ -1,6 +1,4 @@
-"""
-This module provides statistical tests usable on groups of traces sample-wise (Welch's and Student's t-test, ...).
-"""
+"""This module provides statistical tests usable on groups of traces sample-wise (Welch's and Student's t-test, ...)."""
from typing import Sequence, Optional, Tuple
import numpy as np
@@ -31,7 +29,8 @@ def welch_ttest(
p_value: bool = False,
) -> Optional[Tuple[CombinedTrace, ...]]:
"""
- Perform the Welch's t-test sample wise on two sets of traces `first_set` and `second_set`.
+ Perform the Welch's t-test sample wise on two sets of traces :paramref:`~.welch_ttest.first_set` and :paramref:`~.welch_ttest.second_set`.
+
Useful for Test Vector Leakage Analysis (TVLA).
:param first_set:
@@ -76,7 +75,8 @@ def student_ttest(
first_set: Sequence[Trace], second_set: Sequence[Trace]
) -> Optional[CombinedTrace]:
"""
- Perform the Students's t-test sample wise on two sets of traces `first_set` and `second_set`.
+ Perform the Students's t-test sample wise on two sets of traces :paramref:`~.student_ttest.first_set` and :paramref:`~.student_ttest.second_set`.
+
Useful for Test Vector Leakage Analysis (TVLA).
:param first_set:
@@ -91,8 +91,7 @@ def ks_test(
first_set: Sequence[Trace], second_set: Sequence[Trace]
) -> Optional[CombinedTrace]:
"""
- Perform the Kolmogorov-Smirnov two sample test on equality of distributions sample wise on
- two sets of traces `first_set` and `second_set`.
+ Perform the Kolmogorov-Smirnov two sample test on equality of distributions sample wise on two sets of traces :paramref:`~.ks_test.first_set` and :paramref:`~.ks_test.second_set`.
:param first_set:
:param second_set:
diff --git a/pyecsca/sca/trace/trace.py b/pyecsca/sca/trace/trace.py
index 68259c2..27462c7 100644
--- a/pyecsca/sca/trace/trace.py
+++ b/pyecsca/sca/trace/trace.py
@@ -1,6 +1,4 @@
-"""
-This module provides the Trace class.
-"""
+"""This module provides the Trace class."""
import weakref
from typing import Any, Mapping, Sequence
from copy import copy, deepcopy
@@ -12,7 +10,7 @@ from public import public
@public
class Trace(object):
- """A trace, which has some samples and metadata."""
+ """Trace, which has some samples and metadata."""
meta: Mapping[str, Any]
samples: ndarray
@@ -51,18 +49,14 @@ class Trace(object):
@property
def trace_set(self) -> Any:
- """
- The trace set this trace is contained in, if any.
- """
+ """Return the trace set this trace is contained in, if any."""
if self._trace_set is None:
return None
return self._trace_set()
@trace_set.setter
def trace_set(self, trace_set: Any):
- """
- Set the trace set of this trace.
- """
+ """Set the trace set of this trace."""
if trace_set is None:
self._trace_set = None
else:
@@ -107,7 +101,7 @@ class Trace(object):
@public
class CombinedTrace(Trace):
- """A trace that was combined from other traces, `parents`."""
+ """Trace that was combined from other traces, :paramref:`~.CombinedTrace.parents`."""
def __init__(
self,
diff --git a/pyecsca/sca/trace_set/base.py b/pyecsca/sca/trace_set/base.py
index 097d666..e0224fb 100644
--- a/pyecsca/sca/trace_set/base.py
+++ b/pyecsca/sca/trace_set/base.py
@@ -1,6 +1,4 @@
-"""
-This module provides a base traceset class.
-"""
+"""This module provides a base traceset class."""
from pathlib import Path
from typing import List, Union, BinaryIO
@@ -11,7 +9,7 @@ from ..trace import Trace
@public
class TraceSet(object):
- """A set of traces with some metadata."""
+ """Set of traces with some metadata."""
_traces: List[Trace]
_keys: List
diff --git a/pyecsca/sca/trace_set/hdf5.py b/pyecsca/sca/trace_set/hdf5.py
index 09ad8a9..bd3521b 100644
--- a/pyecsca/sca/trace_set/hdf5.py
+++ b/pyecsca/sca/trace_set/hdf5.py
@@ -1,6 +1,7 @@
"""
-This module provides a traceset implemented on top of the Hierarchical Data Format (HDF5). This traceset
-can be loaded "inplace" which means that it is not fully loaded into memory, and only parts of traces that
+This module provides a traceset implemented on top of the Hierarchical Data Format (HDF5).
+
+This traceset can be loaded "inplace" which means that it is not fully loaded into memory, and only parts of traces that
are operated on are in memory. This is very useful for working with huge sets of traces that do not fit in memory.
"""
import pickle
@@ -55,7 +56,7 @@ class HDF5Meta(MutableMapping):
@public
class HDF5TraceSet(TraceSet):
- """A traceset based on the HDF5 (Hierarchical Data Format)."""
+ """Traceset based on the HDF5 (Hierarchical Data Format)."""
_file: Optional[h5py.File]
_ordering: List[str]
diff --git a/pyecsca/sca/trace_set/inspector.py b/pyecsca/sca/trace_set/inspector.py
index 83bbade..101ab2a 100644
--- a/pyecsca/sca/trace_set/inspector.py
+++ b/pyecsca/sca/trace_set/inspector.py
@@ -1,6 +1,4 @@
-"""
-This module provides a traceset implementation based on Riscure's Inspector traceset format (`.trs`).
-"""
+"""This module provides a traceset implementation based on Riscure's Inspector traceset format (``.trs``)."""
import struct
from enum import IntEnum
from io import BytesIO, RawIOBase, BufferedIOBase, UnsupportedOperation
@@ -270,7 +268,7 @@ class InspectorTraceSet(TraceSet):
@property
def sampling_frequency(self) -> int:
- """The sampling frequency of the trace set."""
+ """Return the sampling frequency of the trace set."""
return int(1 / self.x_scale)
def __repr__(self):
diff --git a/pyecsca/sca/trace_set/pickle.py b/pyecsca/sca/trace_set/pickle.py
index 6abe88a..08def8a 100644
--- a/pyecsca/sca/trace_set/pickle.py
+++ b/pyecsca/sca/trace_set/pickle.py
@@ -1,6 +1,7 @@
"""
-This module provides a traceset implementation based on Python's pickle format. This implementation of the
-traceset is thus very generic.
+This module provides a traceset implementation based on Python's pickle format.
+
+This implementation of the traceset is thus very generic.
"""
import pickle
from io import BufferedIOBase, RawIOBase