aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.pre-commit-config.yaml2
-rw-r--r--Makefile30
-rw-r--r--README.md5
-rw-r--r--docs/index.rst6
-rw-r--r--pyecsca/ec/configuration.py75
-rw-r--r--pyecsca/ec/context.py21
-rw-r--r--pyecsca/ec/coordinates.py23
-rw-r--r--pyecsca/ec/curve.py39
-rw-r--r--pyecsca/ec/formula.py78
-rw-r--r--pyecsca/ec/key_agreement.py78
-rw-r--r--pyecsca/ec/key_generation.py6
-rw-r--r--pyecsca/ec/mod.py13
-rw-r--r--pyecsca/ec/model.py2
-rw-r--r--pyecsca/ec/mult.py159
-rw-r--r--pyecsca/ec/op.py25
-rw-r--r--pyecsca/ec/params.py99
-rw-r--r--pyecsca/ec/point.py37
-rw-r--r--pyecsca/ec/signature.py128
-rw-r--r--pyecsca/ec/transformations.py55
-rw-r--r--pyecsca/misc/cfg.py2
-rw-r--r--pyecsca/sca/re/rpa.py14
-rw-r--r--pyecsca/sca/scope/base.py21
-rw-r--r--pyecsca/sca/scope/chipwhisperer.py28
-rw-r--r--pyecsca/sca/scope/picoscope_alt.py34
-rw-r--r--pyecsca/sca/scope/picoscope_sdk.py324
-rw-r--r--pyecsca/sca/target/ISO7816.py45
-rw-r--r--pyecsca/sca/target/PCSC.py2
-rw-r--r--pyecsca/sca/target/binary.py14
-rw-r--r--pyecsca/sca/target/chipwhisperer.py4
-rw-r--r--pyecsca/sca/target/ectester.py355
-rw-r--r--pyecsca/sca/target/simpleserial.py7
-rw-r--r--pyecsca/sca/trace/align.py103
-rw-r--r--pyecsca/sca/trace/combine.py12
-rw-r--r--pyecsca/sca/trace/edit.py11
-rw-r--r--pyecsca/sca/trace/filter.py26
-rw-r--r--pyecsca/sca/trace/match.py4
-rw-r--r--pyecsca/sca/trace/plot.py6
-rw-r--r--pyecsca/sca/trace/process.py20
-rw-r--r--pyecsca/sca/trace/sampling.py29
-rw-r--r--pyecsca/sca/trace/test.py25
-rw-r--r--pyecsca/sca/trace/trace.py21
-rw-r--r--pyecsca/sca/trace_set/base.py6
-rw-r--r--pyecsca/sca/trace_set/chipwhisperer.py20
-rw-r--r--pyecsca/sca/trace_set/hdf5.py27
-rw-r--r--pyecsca/sca/trace_set/inspector.py67
-rw-r--r--setup.py2
-rwxr-xr-xtest/ec/perf_formula.py43
-rwxr-xr-xtest/ec/perf_mod.py37
-rwxr-xr-xtest/ec/perf_mult.py23
-rw-r--r--test/ec/test_configuration.py67
-rw-r--r--test/ec/test_context.py23
-rw-r--r--test/ec/test_curve.py93
-rw-r--r--test/ec/test_formula.py52
-rw-r--r--test/ec/test_key_agreement.py33
-rw-r--r--test/ec/test_key_generation.py1
-rw-r--r--test/ec/test_mod.py99
-rw-r--r--test/ec/test_model.py9
-rw-r--r--test/ec/test_mult.py312
-rw-r--r--test/ec/test_naf.py1
-rw-r--r--test/ec/test_op.py36
-rw-r--r--test/ec/test_params.py45
-rw-r--r--test/ec/test_point.py83
-rw-r--r--test/ec/test_regress.py15
-rw-r--r--test/ec/test_signature.py63
-rw-r--r--test/ec/test_transformations.py1
-rw-r--r--test/ec/utils.py15
-rw-r--r--test/sca/test_align.py105
-rw-r--r--test/sca/test_combine.py21
-rw-r--r--test/sca/test_edit.py26
-rw-r--r--test/sca/test_filter.py17
-rw-r--r--test/sca/test_match.py66
-rw-r--r--test/sca/test_plot.py10
-rw-r--r--test/sca/test_process.py17
-rw-r--r--test/sca/test_rpa.py36
-rw-r--r--test/sca/test_sampling.py139
-rw-r--r--test/sca/test_target.py425
-rw-r--r--test/sca/test_test.py14
-rw-r--r--test/sca/test_trace.py1
-rw-r--r--test/sca/test_traceset.py31
-rw-r--r--test/sca/utils.py1
80 files changed, 2996 insertions, 1074 deletions
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index e940b0b..2ac2274 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -19,4 +19,4 @@ repos:
rev: 3.9.0
hooks:
- id: flake8
- args: ["--ignore=E501,F405,F403,F401,E126"]
+ args: ["--extend-ignore=E501,F405,F403,F401,E126,E203"]
diff --git a/Makefile b/Makefile
index 9acd9c6..326e80e 100644
--- a/Makefile
+++ b/Makefile
@@ -25,10 +25,16 @@ typecheck-all:
mypy pyecsca test --ignore-missing-imports --show-error-codes
codestyle:
- flake8 --ignore=E501,F405,F403,F401,E126 pyecsca
+ flake8 --extend-ignore=E501,F405,F403,F401,E126,E203 pyecsca
codestyle-all:
- flake8 --ignore=E501,F405,F403,F401,E126 pyecsca test
+ flake8 --extend-ignore=E501,F405,F403,F401,E126,E203 pyecsca test
+
+black:
+ black pyecsca
+
+black-all:
+ black pyecsca test
perf: ${PERF_SCRIPTS}
mkdir -p .perf
@@ -41,4 +47,22 @@ docs:
$(MAKE) -C docs apidoc
$(MAKE) -C docs html
-.PHONY: test test-plots test-all typecheck typecheck-all codestyle codestyle-all perf doc-coverage docs
+help:
+ @echo "pyecsca, Python Elliptic Curve cryptography Side-Channel Analysis toolkit."
+ @echo
+ @echo "Available targets:"
+ @echo " - test: Test pyecsca."
+ @echo " - test-plots: Test pyecsca and produce debugging plots."
+ @echo " - test-all: Test pyecsca but also run slow (and disabled) tests."
+ @echo " - typecheck: Use mypy to verify the use of types in pyecsca."
+ @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 " - 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/)."
+ @echo " - doc-coverage: Use interrogate to check documentation coverage of public API."
+ @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
diff --git a/README.md b/README.md
index f3061ff..3cd7584 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# ![](docs/_static/logo_black_full.png)
-[![docs](https://img.shields.io/badge/docs-neuromancer.sk-brightgreen.svg)](https://neuromancer.sk/pyecsca/) [![License MIT ](https://img.shields.io/github/license/J08nY/pyecsca?color=brightgreen)](https://github.com/J08nY/pyecsca/blob/master/LICENSE) ![Test](https://github.com/J08nY/pyecsca/workflows/Test/badge.svg) ![Lint](https://github.com/J08nY/pyecsca/workflows/Lint/badge.svg) [![Codecov](https://img.shields.io/codecov/c/gh/J08nY/pyecsca?color=brightgreen&logo=codecov)](https://codecov.io/gh/J08nY/pyecsca) ![](https://img.shields.io/static/v1?label=mypy&message=No%20issues&color=brightgreen)
+[![docs](https://img.shields.io/badge/docs-neuromancer.sk-brightgreen.svg)](https://neuromancer.sk/pyecsca/) [![License MIT ](https://img.shields.io/github/license/J08nY/pyecsca?color=brightgreen)](https://github.com/J08nY/pyecsca/blob/master/LICENSE) ![Test](https://github.com/J08nY/pyecsca/workflows/Test/badge.svg) ![Lint](https://github.com/J08nY/pyecsca/workflows/Lint/badge.svg) [![Codecov](https://img.shields.io/codecov/c/gh/J08nY/pyecsca?color=brightgreen&logo=codecov)](https://codecov.io/gh/J08nY/pyecsca) ![](https://img.shields.io/static/v1?label=mypy&message=No%20issues&color=brightgreen) [![DeepSource](https://deepsource.io/gh/J08nY/pyecsca.svg/?label=active+issues&show_trend=true)](https://deepsource.io/gh/J08nY/pyecsca/?ref=repository-badge)
**Py**thon **E**lliptic **C**urve cryptography **S**ide-**C**hannel **A**nalysis toolkit.
@@ -60,6 +60,8 @@ 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.
+
- [nose2](https://nose2.readthedocs.io)
- [green](https://github.com/CleanCut/green)
- [mypy](http://mypy-lang.org/)
@@ -69,6 +71,7 @@ It also supports working with [Riscure](https://www.riscure.com) Inspector trace
- [interrogate](https://interrogate.readthedocs.io/)
- [pyinstrument](https://github.com/joerick/pyinstrument/)
- [pre-commit](https://pre-commit.com/) at `.pre-commit-config.yaml`
+ - [black](https://github.com/psf/black)
### Docs
diff --git a/docs/index.rst b/docs/index.rst
index 4ec985c..bde29be 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -13,6 +13,8 @@ pyecsca [pɪɛtska]
.. image:: https://img.shields.io/codecov/c/gh/J08nY/pyecsca?color=brightgreen&logo=codecov
:target: https://codecov.io/gh/J08nY/pyecsca
.. image:: https://img.shields.io/static/v1?label=mypy&message=No%20issues&color=brightgreen
+.. image:: https://deepsource.io/gh/J08nY/pyecsca.svg/?label=active+issues&show_trend=true
+ :target: https://deepsource.io/gh/J08nY/pyecsca/?ref=repository-badge
**Py**\ thon **E**\ lliptic **C**\ urve cryptography **S**\ ide-**C**\ hannel **A**\ nalysis toolkit.
@@ -107,6 +109,8 @@ Testing
- coverage_
- interrogate_
- pyinstrument_
+ - pre-commit_
+ - black_
Docs
----
@@ -169,6 +173,8 @@ this support is very appreciated.
.. _coverage: https://coverage.readthedocs.io/
.. _interrogate: https://interrogate.readthedocs.io/
.. _pyinstrument: https://github.com/joerick/pyinstrument/
+.. _pre-commit: https://pre-commit.com
+.. _black: https://github.com/psf/black
.. _sphinx: https://www.sphinx-doc.org/
.. _sphinx-autodoc-typehints: https://pypi.org/project/sphinx-autodoc-typehints/
.. _nbsphinx: https://nbsphinx.readthedocs.io/
diff --git a/pyecsca/ec/configuration.py b/pyecsca/ec/configuration.py
index 04f8fc7..bf20ec7 100644
--- a/pyecsca/ec/configuration.py
+++ b/pyecsca/ec/configuration.py
@@ -34,6 +34,7 @@ class EnumDefine(Enum):
@public
class Multiplication(EnumDefine):
"""Base multiplication algorithm to use."""
+
TOOM_COOK = "MUL_TOOM_COOK"
KARATSUBA = "MUL_KARATSUBA"
COMBA = "MUL_COMBA"
@@ -43,6 +44,7 @@ class Multiplication(EnumDefine):
@public
class Squaring(EnumDefine):
"""Base squaring algorithm to use."""
+
TOOM_COOK = "SQR_TOOM_COOK"
KARATSUBA = "SQR_KARATSUBA"
COMBA = "SQR_COMBA"
@@ -52,6 +54,7 @@ class Squaring(EnumDefine):
@public
class Reduction(EnumDefine):
"""Modular reduction method used."""
+
BARRETT = "RED_BARRETT"
MONTGOMERY = "RED_MONTGOMERY"
BASE = "RED_BASE"
@@ -60,6 +63,7 @@ class Reduction(EnumDefine):
@public
class Inversion(EnumDefine):
"""Inversion algorithm used."""
+
GCD = "INV_GCD"
EULER = "INV_EULER"
@@ -67,6 +71,7 @@ class Inversion(EnumDefine):
@public
class HashType(EnumDefine):
"""Hash algorithm used in ECDH and ECDSA."""
+
NONE = "HASH_NONE"
SHA1 = "HASH_SHA1"
SHA224 = "HASH_SHA224"
@@ -78,6 +83,7 @@ class HashType(EnumDefine):
@public
class RandomMod(EnumDefine):
"""Method of sampling a uniform integer modulo order."""
+
SAMPLE = "MOD_RAND_SAMPLE"
REDUCE = "MOD_RAND_REDUCE"
@@ -86,6 +92,7 @@ class RandomMod(EnumDefine):
@dataclass(frozen=True)
class Configuration(object):
"""An ECC implementation configuration."""
+
model: CurveModel
coords: CoordinateModel
formulas: FrozenSet[Formula]
@@ -120,8 +127,11 @@ def all_configurations(**kwargs) -> Generator[Configuration, Configuration, None
"""
def is_optional(arg_type):
- return get_origin(arg_type) == Union and len(get_args(arg_type)) == 2 and \
- get_args(arg_type)[1] == type(None) # noqa
+ return (
+ get_origin(arg_type) == Union
+ and len(get_args(arg_type)) == 2
+ and get_args(arg_type)[1] == type(None) # noqa
+ )
def leaf_subclasses(cls):
subs = cls.__subclasses__()
@@ -140,7 +150,7 @@ def all_configurations(**kwargs) -> Generator[Configuration, Configuration, None
"mult": Multiplication,
"sqr": Squaring,
"red": Reduction,
- "inv": Inversion
+ "inv": Inversion,
}
keys = list(filter(lambda key: key not in kwargs, options.keys()))
values = [options[key] for key in keys]
@@ -150,7 +160,11 @@ def all_configurations(**kwargs) -> Generator[Configuration, Configuration, None
def multipliers(mult_classes, coords_formulas, fixed_args=None):
for mult_cls in mult_classes:
- if fixed_args is not None and "cls" in fixed_args and mult_cls != fixed_args["cls"]:
+ if (
+ fixed_args is not None
+ and "cls" in fixed_args
+ and mult_cls != fixed_args["cls"]
+ ):
continue
arg_options = {}
for name, required_type in get_type_hints(mult_cls.__init__).items():
@@ -160,17 +174,30 @@ def all_configurations(**kwargs) -> Generator[Configuration, Configuration, None
if is_optional(required_type):
opt_type = get_args(required_type)[0]
if issubclass(opt_type, Formula):
- options = [formula for formula in coords_formulas if
- isinstance(formula, opt_type)] + [None]
+ options = [
+ formula
+ for formula in coords_formulas
+ if isinstance(formula, opt_type)
+ ] + [None]
else:
options = [None] # TODO: anything here?
- elif get_origin(required_type) is None and issubclass(required_type, Formula):
- options = [formula for formula in coords_formulas if
- isinstance(formula, required_type)]
- elif get_origin(required_type) is None and issubclass(required_type, bool):
+ elif get_origin(required_type) is None and issubclass(
+ required_type, Formula
+ ):
+ options = [
+ formula
+ for formula in coords_formulas
+ if isinstance(formula, required_type)
+ ]
+ elif get_origin(required_type) is None and issubclass(
+ required_type, bool
+ ):
options = [True, False]
- elif get_origin(required_type) is None and issubclass(required_type,
- int) and name == "width":
+ elif (
+ get_origin(required_type) is None
+ and issubclass(required_type, int)
+ and name == "width"
+ ):
options = [3, 5]
else:
options = []
@@ -198,19 +225,29 @@ def all_configurations(**kwargs) -> Generator[Configuration, Configuration, None
if "scalarmult" in kwargs:
if isinstance(kwargs["scalarmult"], ScalarMultiplier):
mults = [kwargs["scalarmult"]]
- if not set(kwargs["scalarmult"].formulas.values()).issubset(coords_formulas):
+ if not set(kwargs["scalarmult"].formulas.values()).issubset(
+ coords_formulas
+ ):
continue
- elif isinstance(kwargs["scalarmult"], type) and issubclass(kwargs["scalarmult"],
- ScalarMultiplier):
+ elif isinstance(kwargs["scalarmult"], type) and issubclass(
+ kwargs["scalarmult"], ScalarMultiplier
+ ):
mult_classes = list(
- filter(lambda mult: issubclass(mult, kwargs["scalarmult"]),
- mult_classes))
+ filter(
+ lambda mult: issubclass(mult, kwargs["scalarmult"]),
+ mult_classes,
+ )
+ )
mults = multipliers(mult_classes, coords_formulas)
else:
- mults = multipliers(mult_classes, coords_formulas, kwargs["scalarmult"])
+ mults = multipliers(
+ mult_classes, coords_formulas, kwargs["scalarmult"]
+ )
else:
mults = multipliers(mult_classes, coords_formulas)
for mult in mults:
formulas = frozenset(mult.formulas.values())
for independent_args in independents(kwargs):
- yield Configuration(model, coords, formulas, mult, **independent_args)
+ yield Configuration(
+ model, coords, formulas, mult, **independent_args
+ )
diff --git a/pyecsca/ec/context.py b/pyecsca/ec/context.py
index 3b38584..5bef891 100644
--- a/pyecsca/ec/context.py
+++ b/pyecsca/ec/context.py
@@ -23,6 +23,7 @@ from public import public
@public
class Action(object):
"""An Action."""
+
inside: bool
def __init__(self):
@@ -41,6 +42,7 @@ class Action(object):
@public
class ResultAction(Action):
"""An action that has a result."""
+
_result: Any = None
_has_result: bool = False
@@ -60,7 +62,12 @@ class ResultAction(Action):
return result
def __exit__(self, exc_type, exc_val, exc_tb):
- if not self._has_result and exc_type is None and exc_val is None and exc_tb is None:
+ if (
+ not self._has_result
+ and exc_type is None
+ and exc_val is None
+ and exc_tb is None
+ ):
raise RuntimeError("Result unset on action exit")
super().__exit__(exc_type, exc_val, exc_tb)
@@ -167,6 +174,7 @@ class NullContext(Context):
@public
class DefaultContext(Context):
"""A context that traces executions of actions in a tree."""
+
actions: Tree
current: List[Action]
@@ -190,6 +198,7 @@ class DefaultContext(Context):
@public
class PathContext(Context):
"""A context that traces targeted actions."""
+
path: List[int]
current: List[int]
current_depth: int
@@ -212,7 +221,7 @@ class PathContext(Context):
else:
self.current[self.current_depth] += 1
self.current_depth += 1
- if self.path == self.current[:self.current_depth]:
+ if self.path == self.current[: self.current_depth]:
self.value = action
def exit_action(self, action: Action) -> None:
@@ -221,10 +230,14 @@ class PathContext(Context):
self.current_depth -= 1
def __repr__(self):
- return f"{self.__class__.__name__}({self.current!r}, depth={self.current_depth!r})"
+ return (
+ f"{self.__class__.__name__}({self.current!r}, depth={self.current_depth!r})"
+ )
-_actual_context: ContextVar[Context] = ContextVar("operational_context", default=NullContext())
+_actual_context: ContextVar[Context] = ContextVar(
+ "operational_context", default=NullContext()
+)
class _ContextManager(object):
diff --git a/pyecsca/ec/coordinates.py b/pyecsca/ec/coordinates.py
index 7c76746..24a0b1a 100644
--- a/pyecsca/ec/coordinates.py
+++ b/pyecsca/ec/coordinates.py
@@ -8,14 +8,23 @@ from typing import List, Any, MutableMapping
from pkg_resources import resource_listdir, resource_isdir, resource_stream
from public import public
-from .formula import (Formula, EFDFormula, AdditionEFDFormula, DoublingEFDFormula,
- TriplingEFDFormula, DifferentialAdditionEFDFormula, LadderEFDFormula,
- ScalingEFDFormula, NegationEFDFormula)
+from .formula import (
+ Formula,
+ EFDFormula,
+ AdditionEFDFormula,
+ DoublingEFDFormula,
+ TriplingEFDFormula,
+ DifferentialAdditionEFDFormula,
+ LadderEFDFormula,
+ ScalingEFDFormula,
+ NegationEFDFormula,
+)
@public
class CoordinateModel(object):
"""A coordinate system for a particular model(form) of an elliptic curve."""
+
name: str
"""Name of the coordinate model"""
full_name: str
@@ -37,7 +46,7 @@ class CoordinateModel(object):
"""Formulas available on the coordinate system."""
def __repr__(self):
- return f"{self.__class__.__name__}(\"{self.name}\" on {self.curve_model.name})"
+ return f'{self.__class__.__name__}("{self.name}" on {self.curve_model.name})'
@public
@@ -61,7 +70,6 @@ class AffineCoordinateModel(CoordinateModel):
class EFDCoordinateModel(CoordinateModel):
-
def __init__(self, dir_path: str, name: str, curve_model: Any):
self.name = name
self.curve_model = curve_model
@@ -89,7 +97,7 @@ class EFDCoordinateModel(CoordinateModel):
"diffadd": DifferentialAdditionEFDFormula,
"ladder": LadderEFDFormula,
"scaling": ScalingEFDFormula,
- "negation": NegationEFDFormula
+ "negation": NegationEFDFormula,
}
cls = formula_types.get(formula_type, EFDFormula)
self.formulas[fname] = cls(join(dir_path, fname), fname, self)
@@ -118,7 +126,8 @@ class EFDCoordinateModel(CoordinateModel):
self.parameters.append(line[10:])
elif line.startswith("assume"):
self.assumptions.append(
- parse(line[7:].replace("^", "**"), mode="exec"))
+ parse(line[7:].replace("^", "**"), mode="exec")
+ )
line = f.readline().decode("ascii").rstrip()
def __eq__(self, other):
diff --git a/pyecsca/ec/curve.py b/pyecsca/ec/curve.py
index 4cfb978..bfa58c8 100644
--- a/pyecsca/ec/curve.py
+++ b/pyecsca/ec/curve.py
@@ -16,6 +16,7 @@ from .point import Point, InfinityPoint
@public
class EllipticCurve(object):
"""An elliptic curve."""
+
model: CurveModel
"""The model of the curve."""
coordinate_model: CoordinateModel
@@ -27,11 +28,23 @@ class EllipticCurve(object):
neutral: Point
"""The neutral point on the curve."""
- def __init__(self, model: CurveModel, coordinate_model: CoordinateModel,
- prime: int, neutral: Point, parameters: MutableMapping[str, Union[Mod, int]]):
- if coordinate_model not in model.coordinates.values() and not isinstance(coordinate_model, AffineCoordinateModel):
+ def __init__(
+ self,
+ model: CurveModel,
+ coordinate_model: CoordinateModel,
+ prime: int,
+ neutral: Point,
+ parameters: MutableMapping[str, Union[Mod, int]],
+ ):
+ if coordinate_model not in model.coordinates.values() and not isinstance(
+ coordinate_model, AffineCoordinateModel
+ ):
raise ValueError
- if set(model.parameter_names).union(coordinate_model.parameters).symmetric_difference(parameters.keys()):
+ if (
+ set(model.parameter_names)
+ .union(coordinate_model.parameters)
+ .symmetric_difference(parameters.keys())
+ ):
raise ValueError
self.model = model
self.coordinate_model = coordinate_model
@@ -52,8 +65,11 @@ class EllipticCurve(object):
raise ValueError("Coordinate model of point is not affine.")
if point.coordinate_model.curve_model != self.model:
raise ValueError("Curve model of point does not match the curve.")
- locls = {var + str(i + 1): point.coords[var]
- for i, point in enumerate(points) for var in point.coords}
+ locls = {
+ var + str(i + 1): point.coords[var]
+ for i, point in enumerate(points)
+ for var in point.coords
+ }
locls.update(self.parameters)
for line in formulas:
exec(compile(line, "", mode="exec"), None, locls)
@@ -228,7 +244,9 @@ class EllipticCurve(object):
else:
raise NotImplementedError
else:
- raise ValueError(f"Wrong encoding type: {hex(encoded[0])}, should be one of 0x04, 0x06, 0x02, 0x03 or 0x00")
+ raise ValueError(
+ f"Wrong encoding type: {hex(encoded[0])}, should be one of 0x04, 0x06, 0x02, 0x03 or 0x00"
+ )
def affine_random(self) -> Point:
"""Generate a random affine point on the curve."""
@@ -246,7 +264,12 @@ class EllipticCurve(object):
def __eq__(self, other):
if not isinstance(other, EllipticCurve):
return False
- return self.model == other.model and self.coordinate_model == other.coordinate_model and self.prime == other.prime and self.parameters == other.parameters
+ return (
+ self.model == other.model
+ and self.coordinate_model == other.coordinate_model
+ and self.prime == other.prime
+ and self.parameters == other.parameters
+ )
def __str__(self):
return "EllipticCurve"
diff --git a/pyecsca/ec/formula.py b/pyecsca/ec/formula.py
index d0435f8..c0505e1 100644
--- a/pyecsca/ec/formula.py
+++ b/pyecsca/ec/formula.py
@@ -21,6 +21,7 @@ from ..misc.cfg import getconfig
@public
class OpResult(object):
"""A result of an operation."""
+
parents: Tuple
op: OpType
name: str
@@ -44,6 +45,7 @@ class OpResult(object):
@public
class FormulaAction(ResultAction):
"""An execution of a formula, on some input points and parameters, with some outputs."""
+
formula: "Formula"
"""The formula that was executed."""
inputs: MutableMapping[str, Mod]
@@ -95,6 +97,7 @@ class FormulaAction(ResultAction):
@public
class Formula(ABC):
"""A formula operating on points."""
+
name: str
"""Name of the formula."""
shortname: ClassVar[str]
@@ -131,7 +134,9 @@ class Formula(ABC):
raise ValueError(f"Wrong coordinate model of point {point}.")
for coord, value in point.coords.items():
if not isinstance(value, Mod) or value.n != field:
- raise ValueError(f"Wrong coordinate input {coord} = {value} of point {i}.")
+ raise ValueError(
+ f"Wrong coordinate input {coord} = {value} of point {i}."
+ )
params[coord + str(i + 1)] = value
def __validate_assumptions(self, field, params):
@@ -146,16 +151,22 @@ class Formula(ABC):
holds = eval(compiled, None, alocals)
if not holds:
# The assumption doesn't hold, see what is the current configured action and do it.
- raise_unsatisified_assumption(getconfig().ec.unsatisfied_formula_assumption_action,
- f"Unsatisfied assumption in the formula ({assumption_string}).")
+ raise_unsatisified_assumption(
+ getconfig().ec.unsatisfied_formula_assumption_action,
+ f"Unsatisfied assumption in the formula ({assumption_string}).",
+ )
else:
k = FF(field)
expr = sympify(f"{rhs} - {lhs}", evaluate=False)
for curve_param, value in params.items():
expr = expr.subs(curve_param, k(value))
- if len(expr.free_symbols) > 1 or (param := str(expr.free_symbols.pop())) not in self.parameters:
+ if (
+ len(expr.free_symbols) > 1
+ or (param := str(expr.free_symbols.pop())) not in self.parameters
+ ):
raise ValueError(
- f"This formula couldn't be executed due to an unsupported assumption ({assumption_string}).")
+ f"This formula couldn't be executed due to an unsupported assumption ({assumption_string})."
+ )
def resolve(expression):
if not expression.args:
@@ -178,7 +189,9 @@ class Formula(ABC):
params[param] = Mod(int(root), field)
break
else:
- raise UnsatisfiedAssumptionError(f"Unsatisfied assumption in the formula ({assumption_string}).")
+ raise UnsatisfiedAssumptionError(
+ f"Unsatisfied assumption in the formula ({assumption_string})."
+ )
def __call__(self, field: int, *points: Any, **params: Mod) -> Tuple[Any, ...]:
"""
@@ -190,6 +203,7 @@ class Formula(ABC):
:return: The resulting point(s).
"""
from .point import Point
+
self.__validate_params(field, params)
self.__validate_points(field, points, params)
self.__validate_assumptions(field, params)
@@ -201,7 +215,9 @@ class Formula(ABC):
# TODO: This is not general enough, if for example the op is `t = 1/2`, it will be float.
# Temporarily, add an assertion that this does not happen so we do not give bad results.
if isinstance(op_result, float):
- raise AssertionError(f"Bad stuff happened in op {op}, floats will pollute the results.")
+ raise AssertionError(
+ f"Bad stuff happened in op {op}, floats will pollute the results."
+ )
if not isinstance(op_result, Mod):
op_result = Mod(op_result, field)
action.add_operation(op, op_result)
@@ -285,8 +301,14 @@ class Formula(ABC):
@property
def num_addsubs(self) -> int:
"""Number of additions and subtractions."""
- return len(list(
- filter(lambda op: op.operator == OpType.Add or op.operator == OpType.Sub, self.code)))
+ return len(
+ list(
+ filter(
+ lambda op: op.operator == OpType.Add or op.operator == OpType.Sub,
+ self.code,
+ )
+ )
+ )
class EFDFormula(Formula):
@@ -313,7 +335,10 @@ class EFDFormula(Formula):
self.parameters.append(line[10:])
elif line.startswith("assume"):
self.assumptions.append(
- parse(line[7:].replace("=", "==").replace("^", "**"), mode="eval"))
+ parse(
+ line[7:].replace("=", "==").replace("^", "**"), mode="eval"
+ )
+ )
elif line.startswith("unified"):
self.unified = True
line = f.readline().decode("ascii").rstrip()
@@ -321,7 +346,9 @@ class EFDFormula(Formula):
def __read_op3_file(self, path):
with resource_stream(__name__, path) as f:
for line in f.readlines():
- code_module = parse(line.decode("ascii").replace("^", "**"), path, mode="exec")
+ code_module = parse(
+ line.decode("ascii").replace("^", "**"), path, mode="exec"
+ )
self.code.append(CodeOp(code_module))
@property
@@ -334,19 +361,29 @@ class EFDFormula(Formula):
@property
def inputs(self):
- return set(var + str(i) for var, i in product(self.coordinate_model.variables,
- range(1, 1 + self.num_inputs)))
+ return set(
+ var + str(i)
+ for var, i in product(
+ self.coordinate_model.variables, range(1, 1 + self.num_inputs)
+ )
+ )
@property
def outputs(self):
- return set(var + str(i) for var, i in product(self.coordinate_model.variables,
- range(self.output_index,
- self.output_index + self.num_outputs)))
+ return set(
+ var + str(i)
+ for var, i in product(
+ self.coordinate_model.variables,
+ range(self.output_index, self.output_index + self.num_outputs),
+ )
+ )
def __eq__(self, other):
if not isinstance(other, EFDFormula):
return False
- return self.name == other.name and self.coordinate_model == other.coordinate_model
+ return (
+ self.name == other.name and self.coordinate_model == other.coordinate_model
+ )
def __hash__(self):
return hash(self.name) + hash(self.coordinate_model)
@@ -355,6 +392,7 @@ class EFDFormula(Formula):
@public
class AdditionFormula(Formula, ABC):
"""A formula that adds two points."""
+
shortname = "add"
num_inputs = 2
num_outputs = 1
@@ -368,6 +406,7 @@ class AdditionEFDFormula(AdditionFormula, EFDFormula):
@public
class DoublingFormula(Formula, ABC):
"""A formula that doubles a point."""
+
shortname = "dbl"
num_inputs = 1
num_outputs = 1
@@ -381,6 +420,7 @@ class DoublingEFDFormula(DoublingFormula, EFDFormula):
@public
class TriplingFormula(Formula, ABC):
"""A formula that triples a point."""
+
shortname = "tpl"
num_inputs = 1
num_outputs = 1
@@ -394,6 +434,7 @@ class TriplingEFDFormula(TriplingFormula, EFDFormula):
@public
class NegationFormula(Formula, ABC):
"""A formula that negates a point."""
+
shortname = "neg"
num_inputs = 1
num_outputs = 1
@@ -407,6 +448,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)."""
+
shortname = "scl"
num_inputs = 1
num_outputs = 1
@@ -423,6 +465,7 @@ class DifferentialAdditionFormula(Formula, ABC):
A 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]`).
"""
+
shortname = "dadd"
num_inputs = 3
num_outputs = 1
@@ -441,6 +484,7 @@ class LadderFormula(Formula, ABC):
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]`).
"""
+
shortname = "ladd"
num_inputs = 3
num_outputs = 2
diff --git a/pyecsca/ec/key_agreement.py b/pyecsca/ec/key_agreement.py
index c9e4a01..b58374f 100644
--- a/pyecsca/ec/key_agreement.py
+++ b/pyecsca/ec/key_agreement.py
@@ -16,12 +16,19 @@ from .point import Point
@public
class ECDHAction(ResultAction):
"""An ECDH key exchange."""
+
params: DomainParameters
hash_algo: Optional[Any]
privkey: Mod
pubkey: Point
- def __init__(self, params: DomainParameters, hash_algo: Optional[Any], privkey: Mod, pubkey: Point):
+ def __init__(
+ self,
+ params: DomainParameters,
+ hash_algo: Optional[Any],
+ privkey: Mod,
+ pubkey: Point,
+ ):
super().__init__()
self.params = params
self.hash_algo = hash_algo
@@ -35,14 +42,21 @@ class ECDHAction(ResultAction):
@public
class KeyAgreement(object):
"""An EC based key agreement primitive. (ECDH)"""
+
mult: ScalarMultiplier
params: DomainParameters
pubkey: Point
privkey: Mod
hash_algo: Optional[Any]
- def __init__(self, mult: ScalarMultiplier, params: DomainParameters, pubkey: Point, privkey: Mod,
- hash_algo: Optional[Any] = None):
+ def __init__(
+ self,
+ mult: ScalarMultiplier,
+ params: DomainParameters,
+ pubkey: Point,
+ privkey: Mod,
+ hash_algo: Optional[Any] = None,
+ ):
self.mult = mult
self.params = params
self.pubkey = pubkey
@@ -65,7 +79,9 @@ class KeyAgreement(object):
:return: The shared secret.
"""
- with ECDHAction(self.params, self.hash_algo, self.privkey, self.pubkey) as action:
+ with ECDHAction(
+ self.params, self.hash_algo, self.privkey, self.pubkey
+ ) as action:
affine_point = self.perform_raw()
x = int(affine_point.x)
p = self.params.curve.prime
@@ -80,8 +96,13 @@ class KeyAgreement(object):
class ECDH_NONE(KeyAgreement):
"""Raw x-coordinate ECDH."""
- def __init__(self, mult: ScalarMultiplier, params: DomainParameters, pubkey: Point,
- privkey: Mod):
+ def __init__(
+ self,
+ mult: ScalarMultiplier,
+ params: DomainParameters,
+ pubkey: Point,
+ privkey: Mod,
+ ):
super().__init__(mult, params, pubkey, privkey)
@@ -89,8 +110,13 @@ class ECDH_NONE(KeyAgreement):
class ECDH_SHA1(KeyAgreement):
"""ECDH with SHA1 of x-coordinate."""
- def __init__(self, mult: ScalarMultiplier, params: DomainParameters, pubkey: Point,
- privkey: Mod):
+ def __init__(
+ self,
+ mult: ScalarMultiplier,
+ params: DomainParameters,
+ pubkey: Point,
+ privkey: Mod,
+ ):
super().__init__(mult, params, pubkey, privkey, hashlib.sha1)
@@ -98,8 +124,13 @@ class ECDH_SHA1(KeyAgreement):
class ECDH_SHA224(KeyAgreement):
"""ECDH with SHA224 of x-coordinate."""
- def __init__(self, mult: ScalarMultiplier, params: DomainParameters, pubkey: Point,
- privkey: Mod):
+ def __init__(
+ self,
+ mult: ScalarMultiplier,
+ params: DomainParameters,
+ pubkey: Point,
+ privkey: Mod,
+ ):
super().__init__(mult, params, pubkey, privkey, hashlib.sha224)
@@ -107,8 +138,13 @@ class ECDH_SHA224(KeyAgreement):
class ECDH_SHA256(KeyAgreement):
"""ECDH with SHA256 of x-coordinate."""
- def __init__(self, mult: ScalarMultiplier, params: DomainParameters, pubkey: Point,
- privkey: Mod):
+ def __init__(
+ self,
+ mult: ScalarMultiplier,
+ params: DomainParameters,
+ pubkey: Point,
+ privkey: Mod,
+ ):
super().__init__(mult, params, pubkey, privkey, hashlib.sha256)
@@ -116,8 +152,13 @@ class ECDH_SHA256(KeyAgreement):
class ECDH_SHA384(KeyAgreement):
"""ECDH with SHA384 of x-coordinate."""
- def __init__(self, mult: ScalarMultiplier, params: DomainParameters, pubkey: Point,
- privkey: Mod):
+ def __init__(
+ self,
+ mult: ScalarMultiplier,
+ params: DomainParameters,
+ pubkey: Point,
+ privkey: Mod,
+ ):
super().__init__(mult, params, pubkey, privkey, hashlib.sha384)
@@ -125,6 +166,11 @@ class ECDH_SHA384(KeyAgreement):
class ECDH_SHA512(KeyAgreement):
"""ECDH with SHA512 of x-coordinate."""
- def __init__(self, mult: ScalarMultiplier, params: DomainParameters, pubkey: Point,
- privkey: Mod):
+ def __init__(
+ self,
+ mult: ScalarMultiplier,
+ params: DomainParameters,
+ pubkey: Point,
+ privkey: Mod,
+ ):
super().__init__(mult, params, pubkey, privkey, hashlib.sha512)
diff --git a/pyecsca/ec/key_generation.py b/pyecsca/ec/key_generation.py
index d506585..a08c767 100644
--- a/pyecsca/ec/key_generation.py
+++ b/pyecsca/ec/key_generation.py
@@ -15,6 +15,7 @@ from .point import Point
@public
class KeygenAction(ResultAction):
"""A key generation."""
+
params: DomainParameters
def __init__(self, params: DomainParameters):
@@ -28,11 +29,14 @@ class KeygenAction(ResultAction):
@public
class KeyGeneration(object):
"""Key generator."""
+
mult: ScalarMultiplier
params: DomainParameters
affine: bool
- def __init__(self, mult: ScalarMultiplier, params: DomainParameters, affine: bool = False):
+ 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.
diff --git a/pyecsca/ec/mod.py b/pyecsca/ec/mod.py
index 8aca8a9..8485cbe 100644
--- a/pyecsca/ec/mod.py
+++ b/pyecsca/ec/mod.py
@@ -125,6 +125,7 @@ def _check(func):
@public
class RandomModAction(ResultAction):
"""A random sampling from Z_n."""
+
order: int
def __init__(self, order: int):
@@ -141,6 +142,7 @@ _mod_classes: Dict[str, Type] = {}
@public
class Mod(object):
"""An element x of ℤₙ."""
+
x: Any
n: Any
@@ -153,7 +155,9 @@ class Mod(object):
if selected_class not in _mod_classes:
# Fallback to something
selected_class = next(iter(_mod_classes.keys()))
- return _mod_classes[selected_class].__new__(_mod_classes[selected_class], *args, **kwargs)
+ return _mod_classes[selected_class].__new__(
+ _mod_classes[selected_class], *args, **kwargs
+ )
@_check
def __add__(self, other):
@@ -254,6 +258,7 @@ class Mod(object):
@public
class RawMod(Mod):
"""An element x of ℤₙ (implemented using Python integers)."""
+
x: int
n: int
@@ -462,6 +467,7 @@ def _symbolic_check(func):
@public
class SymbolicMod(Mod):
"""A symbolic element x of ℤₙ (implemented using sympy)."""
+
x: Expr
n: int
@@ -489,10 +495,10 @@ class SymbolicMod(Mod):
return -self + other
def __neg__(self):
- return self.__class__(- self.x, self.n)
+ return self.__class__(-self.x, self.n)
def inverse(self):
- return self.__class__(self.x**(-1), self.n)
+ return self.__class__(self.x ** (-1), self.n)
def sqrt(self):
raise NotImplementedError
@@ -569,6 +575,7 @@ if has_gmp:
@public
class GMPMod(Mod):
"""An element x of ℤₙ. Implemented by GMP."""
+
x: gmpy2.mpz
n: gmpy2.mpz
diff --git a/pyecsca/ec/model.py b/pyecsca/ec/model.py
index 46b03b7..861dfca 100644
--- a/pyecsca/ec/model.py
+++ b/pyecsca/ec/model.py
@@ -14,6 +14,7 @@ from .coordinates import EFDCoordinateModel, CoordinateModel
@public
class CurveModel(object):
"""A model(form) of an elliptic curve."""
+
name: str
shortname: str
coordinates: MutableMapping[str, CoordinateModel]
@@ -134,6 +135,7 @@ class MontgomeryModel(EFDCurveModel):
B y^2 = x^3 + A x^2 + x
"""
+
def __init__(self):
super().__init__("montgom")
diff --git a/pyecsca/ec/mult.py b/pyecsca/ec/mult.py
index 6fd6feb..7614dd4 100644
--- a/pyecsca/ec/mult.py
+++ b/pyecsca/ec/mult.py
@@ -8,8 +8,15 @@ from typing import Mapping, Tuple, Optional, MutableMapping, ClassVar, Set, Type
from public import public
from .context import ResultAction, Action
-from .formula import (Formula, AdditionFormula, DoublingFormula, DifferentialAdditionFormula,
- ScalingFormula, LadderFormula, NegationFormula)
+from .formula import (
+ Formula,
+ AdditionFormula,
+ DoublingFormula,
+ DifferentialAdditionFormula,
+ ScalingFormula,
+ LadderFormula,
+ NegationFormula,
+)
from .naf import naf, wnaf
from .params import DomainParameters
from .point import Point
@@ -18,6 +25,7 @@ from .point import Point
@public
class ScalarMultiplicationAction(ResultAction):
"""A scalar multiplication of a point on a curve by a scalar."""
+
point: Point
scalar: int
@@ -33,6 +41,7 @@ class ScalarMultiplicationAction(ResultAction):
@public
class PrecomputationAction(Action):
"""A precomputation of a point in scalar multiplication."""
+
params: DomainParameters
point: Point
@@ -51,6 +60,7 @@ class ScalarMultiplier(ABC):
of the point at infinity.
:param formulas: Formulas this instance will use.
"""
+
requires: ClassVar[Set[Type]] # Type[Formula] but mypy has a false positive
"""The set of formulas that the multiplier requires."""
optionals: ClassVar[Set[Type]] # Type[Formula] but mypy has a false positive
@@ -64,8 +74,16 @@ class ScalarMultiplier(ABC):
_initialized: bool = False
def __init__(self, short_circuit: bool = True, **formulas: Optional[Formula]):
- if len(set(formula.coordinate_model for formula in formulas.values() if
- formula is not None)) != 1:
+ if (
+ len(
+ set(
+ formula.coordinate_model
+ for formula in formulas.values()
+ if formula is not None
+ )
+ )
+ != 1
+ ):
raise ValueError
self.short_circuit = short_circuit
self.formulas = {k: v for k, v in formulas.items() if v is not None}
@@ -78,7 +96,9 @@ class ScalarMultiplier(ABC):
return copy(other)
if other == self._params.curve.neutral:
return copy(one)
- return self.formulas["add"](self._params.curve.prime, one, other, **self._params.curve.parameters)[0]
+ return self.formulas["add"](
+ self._params.curve.prime, one, other, **self._params.curve.parameters
+ )[0]
def _dbl(self, point: Point) -> Point:
if "dbl" not in self.formulas:
@@ -86,12 +106,16 @@ class ScalarMultiplier(ABC):
if self.short_circuit:
if point == self._params.curve.neutral:
return copy(point)
- return self.formulas["dbl"](self._params.curve.prime, point, **self._params.curve.parameters)[0]
+ return self.formulas["dbl"](
+ self._params.curve.prime, point, **self._params.curve.parameters
+ )[0]
def _scl(self, point: Point) -> Point:
if "scl" not in self.formulas:
raise NotImplementedError
- return self.formulas["scl"](self._params.curve.prime, point, **self._params.curve.parameters)[0]
+ return self.formulas["scl"](
+ self._params.curve.prime, point, **self._params.curve.parameters
+ )[0]
def _ladd(self, start: Point, to_dbl: Point, to_add: Point) -> Tuple[Point, ...]:
if "ladd" not in self.formulas:
@@ -101,7 +125,13 @@ class ScalarMultiplier(ABC):
return to_dbl, to_add
if to_add == self._params.curve.neutral:
return self._dbl(to_dbl), to_dbl
- return self.formulas["ladd"](self._params.curve.prime, start, to_dbl, to_add, **self._params.curve.parameters)
+ return self.formulas["ladd"](
+ self._params.curve.prime,
+ start,
+ to_dbl,
+ to_add,
+ **self._params.curve.parameters,
+ )
def _dadd(self, start: Point, one: Point, other: Point) -> Point:
if "dadd" not in self.formulas:
@@ -111,12 +141,16 @@ class ScalarMultiplier(ABC):
return copy(other)
if other == self._params.curve.neutral:
return copy(one)
- return self.formulas["dadd"](self._params.curve.prime, start, one, other, **self._params.curve.parameters)[0]
+ return self.formulas["dadd"](
+ self._params.curve.prime, start, one, other, **self._params.curve.parameters
+ )[0]
def _neg(self, point: Point) -> Point:
if "neg" not in self.formulas:
raise NotImplementedError
- return self.formulas["neg"](self._params.curve.prime, point, **self._params.curve.parameters)[0]
+ return self.formulas["neg"](
+ self._params.curve.prime, point, **self._params.curve.parameters
+ )[0]
def init(self, params: DomainParameters, point: Point):
"""
@@ -129,7 +163,10 @@ class ScalarMultiplier(ABC):
:param point: The point to initialize the multiplier with.
"""
coord_model = set(self.formulas.values()).pop().coordinate_model
- if params.curve.coordinate_model != coord_model or point.coordinate_model != coord_model:
+ if (
+ params.curve.coordinate_model != coord_model
+ or point.coordinate_model != coord_model
+ ):
raise ValueError
self._params = params
self._point = point
@@ -156,14 +193,21 @@ class LTRMultiplier(ScalarMultiplier):
The `always` parameter determines whether the double and add always method is used.
"""
+
requires = {AdditionFormula, DoublingFormula}
optionals = {ScalingFormula}
always: bool
complete: bool
- def __init__(self, add: AdditionFormula, dbl: DoublingFormula,
- scl: ScalingFormula = None, always: bool = False, complete: bool = True,
- short_circuit: bool = True):
+ def __init__(
+ self,
+ add: AdditionFormula,
+ dbl: DoublingFormula,
+ scl: ScalingFormula = None,
+ always: bool = False,
+ complete: bool = True,
+ short_circuit: bool = True,
+ ):
super().__init__(short_circuit=short_circuit, add=add, dbl=dbl, scl=scl)
self.always = always
self.complete = complete
@@ -201,12 +245,19 @@ class RTLMultiplier(ScalarMultiplier):
The `always` parameter determines whether the double and add always method is used.
"""
+
requires = {AdditionFormula, DoublingFormula}
optionals = {ScalingFormula}
always: bool
- def __init__(self, add: AdditionFormula, dbl: DoublingFormula,
- scl: ScalingFormula = None, always: bool = False, short_circuit: bool = True):
+ def __init__(
+ self,
+ add: AdditionFormula,
+ dbl: DoublingFormula,
+ scl: ScalingFormula = None,
+ always: bool = False,
+ short_circuit: bool = True,
+ ):
super().__init__(short_circuit=short_circuit, add=add, dbl=dbl, scl=scl)
self.always = always
@@ -240,11 +291,17 @@ class CoronMultiplier(ScalarMultiplier):
https://link.springer.com/content/pdf/10.1007/3-540-48059-5_25.pdf
"""
+
requires = {AdditionFormula, DoublingFormula}
optionals = {ScalingFormula}
- def __init__(self, add: AdditionFormula, dbl: DoublingFormula, scl: ScalingFormula = None,
- short_circuit: bool = True):
+ def __init__(
+ self,
+ add: AdditionFormula,
+ dbl: DoublingFormula,
+ scl: ScalingFormula = None,
+ short_circuit: bool = True,
+ ):
super().__init__(short_circuit=short_circuit, add=add, dbl=dbl, scl=scl)
def multiply(self, scalar: int) -> Point:
@@ -270,12 +327,19 @@ class LadderMultiplier(ScalarMultiplier):
"""
Montgomery ladder multiplier, using a three input, two output ladder formula.
"""
+
requires = {LadderFormula}
optionals = {DoublingFormula, ScalingFormula}
complete: bool
- def __init__(self, ladd: LadderFormula, dbl: DoublingFormula = None, scl: ScalingFormula = None,
- complete: bool = True, short_circuit: bool = True):
+ def __init__(
+ self,
+ ladd: LadderFormula,
+ dbl: DoublingFormula = None,
+ scl: ScalingFormula = None,
+ complete: bool = True,
+ short_circuit: bool = True,
+ ):
super().__init__(short_circuit=short_circuit, ladd=ladd, dbl=dbl, scl=scl)
self.complete = complete
if (not complete or short_circuit) and dbl is None:
@@ -311,12 +375,19 @@ class SimpleLadderMultiplier(ScalarMultiplier):
"""
Montgomery ladder multiplier, using addition and doubling formulas.
"""
+
requires = {AdditionFormula, DoublingFormula}
optionals = {ScalingFormula}
complete: bool
- def __init__(self, add: AdditionFormula, dbl: DoublingFormula, scl: ScalingFormula = None,
- complete: bool = True, short_circuit: bool = True):
+ def __init__(
+ self,
+ add: AdditionFormula,
+ dbl: DoublingFormula,
+ scl: ScalingFormula = None,
+ complete: bool = True,
+ short_circuit: bool = True,
+ ):
super().__init__(short_circuit=short_circuit, add=add, dbl=dbl, scl=scl)
self.complete = complete
@@ -349,12 +420,19 @@ class DifferentialLadderMultiplier(ScalarMultiplier):
"""
Montgomery ladder multiplier, using differential addition and doubling formulas.
"""
+
requires = {DifferentialAdditionFormula, DoublingFormula}
optionals = {ScalingFormula}
complete: bool
- def __init__(self, dadd: DifferentialAdditionFormula, dbl: DoublingFormula,
- scl: ScalingFormula = None, complete: bool = True, short_circuit: bool = True):
+ def __init__(
+ self,
+ dadd: DifferentialAdditionFormula,
+ dbl: DoublingFormula,
+ scl: ScalingFormula = None,
+ complete: bool = True,
+ short_circuit: bool = True,
+ ):
super().__init__(short_circuit=short_circuit, dadd=dadd, dbl=dbl, scl=scl)
self.complete = complete
@@ -388,13 +466,22 @@ class BinaryNAFMultiplier(ScalarMultiplier):
"""
Binary NAF (Non Adjacent Form) multiplier, left-to-right.
"""
+
requires = {AdditionFormula, DoublingFormula, NegationFormula}
optionals = {ScalingFormula}
_point_neg: Point
- def __init__(self, add: AdditionFormula, dbl: DoublingFormula,
- neg: NegationFormula, scl: ScalingFormula = None, short_circuit: bool = True):
- super().__init__(short_circuit=short_circuit, add=add, dbl=dbl, neg=neg, scl=scl)
+ def __init__(
+ self,
+ add: AdditionFormula,
+ dbl: DoublingFormula,
+ neg: NegationFormula,
+ scl: ScalingFormula = None,
+ short_circuit: bool = True,
+ ):
+ super().__init__(
+ short_circuit=short_circuit, add=add, dbl=dbl, neg=neg, scl=scl
+ )
def init(self, params: DomainParameters, point: Point):
with PrecomputationAction(params, point):
@@ -425,6 +512,7 @@ class WindowNAFMultiplier(ScalarMultiplier):
"""
Window NAF (Non Adjacent Form) multiplier, left-to-right.
"""
+
requires = {AdditionFormula, DoublingFormula, NegationFormula}
optionals = {ScalingFormula}
_points: MutableMapping[int, Point]
@@ -432,10 +520,19 @@ class WindowNAFMultiplier(ScalarMultiplier):
precompute_negation: bool = False
width: int
- def __init__(self, add: AdditionFormula, dbl: DoublingFormula,
- neg: NegationFormula, width: int, scl: ScalingFormula = None,
- precompute_negation: bool = False, short_circuit: bool = True):
- super().__init__(short_circuit=short_circuit, add=add, dbl=dbl, neg=neg, scl=scl)
+ def __init__(
+ self,
+ add: AdditionFormula,
+ dbl: DoublingFormula,
+ neg: NegationFormula,
+ width: int,
+ scl: ScalingFormula = None,
+ precompute_negation: bool = False,
+ short_circuit: bool = True,
+ ):
+ super().__init__(
+ short_circuit=short_circuit, add=add, dbl=dbl, neg=neg, scl=scl
+ )
self.width = width
self.precompute_negation = precompute_negation
diff --git a/pyecsca/ec/op.py b/pyecsca/ec/op.py
index 2401624..e500914 100644
--- a/pyecsca/ec/op.py
+++ b/pyecsca/ec/op.py
@@ -1,8 +1,23 @@
"""
This module provides a class for a code operation.
"""
-from ast import (Module, walk, Name, BinOp, UnaryOp, Constant, Mult, Div, Add, Sub, Pow, Assign,
- operator as ast_operator, unaryop as ast_unaryop, USub)
+from ast import (
+ Module,
+ walk,
+ Name,
+ BinOp,
+ UnaryOp,
+ Constant,
+ Mult,
+ Div,
+ Add,
+ Sub,
+ Pow,
+ Assign,
+ operator as ast_operator,
+ unaryop as ast_unaryop,
+ USub,
+)
from enum import Enum
from types import CodeType
from typing import FrozenSet, cast, Any, Optional, Union
@@ -15,6 +30,7 @@ from .mod import Mod
@public
class OpType(Enum):
"""A type of binary and unary operators."""
+
Add = (2, "+")
Sub = (2, "-")
Neg = (1, "-")
@@ -33,6 +49,7 @@ class OpType(Enum):
@public
class CodeOp(object):
"""An operation that can be executed."""
+
result: str
"""The result variable of the operation (e.g. the `r` in `r = 2*a`)."""
parameters: FrozenSet[str]
@@ -90,7 +107,9 @@ class CodeOp(object):
else:
return None
- def __to_op(self, op: Optional[Union[ast_operator, ast_unaryop]], left: Any, right: Any) -> OpType:
+ def __to_op(
+ self, op: Optional[Union[ast_operator, ast_unaryop]], left: Any, right: Any
+ ) -> OpType:
if isinstance(op, Mult):
return OpType.Mult
elif isinstance(op, Div):
diff --git a/pyecsca/ec/params.py b/pyecsca/ec/params.py
index 6583bae..370b8eb 100644
--- a/pyecsca/ec/params.py
+++ b/pyecsca/ec/params.py
@@ -17,8 +17,13 @@ from .coordinates import AffineCoordinateModel, CoordinateModel
from .curve import EllipticCurve
from .error import UnsatisfiedAssumptionError, raise_unsatisified_assumption
from .mod import Mod
-from .model import (CurveModel, ShortWeierstrassModel, MontgomeryModel, EdwardsModel,
- TwistedEdwardsModel)
+from .model import (
+ CurveModel,
+ ShortWeierstrassModel,
+ MontgomeryModel,
+ EdwardsModel,
+ TwistedEdwardsModel,
+)
from .point import Point, InfinityPoint
from ..misc.cfg import getconfig
@@ -26,6 +31,7 @@ from ..misc.cfg import getconfig
@public
class DomainParameters(object):
"""Domain parameters which specify a subgroup on an elliptic curve."""
+
curve: EllipticCurve
generator: Point
order: int
@@ -33,8 +39,15 @@ class DomainParameters(object):
name: Optional[str]
category: Optional[str]
- def __init__(self, curve: EllipticCurve, generator: Point, order: int,
- cofactor: int, name: Optional[str] = None, category: Optional[str] = None):
+ def __init__(
+ self,
+ curve: EllipticCurve,
+ generator: Point,
+ order: int,
+ cofactor: int,
+ name: Optional[str] = None,
+ category: Optional[str] = None,
+ ):
self.curve = curve
self.generator = generator
self.order = order
@@ -45,7 +58,12 @@ class DomainParameters(object):
def __eq__(self, other):
if not isinstance(other, DomainParameters):
return False
- return self.curve == other.curve and self.generator == other.generator and self.order == other.order and self.cofactor == other.cofactor
+ return (
+ self.curve == other.curve
+ and self.generator == other.generator
+ and self.order == other.order
+ and self.cofactor == other.cofactor
+ )
def __get_name(self):
if self.name and self.category:
@@ -69,6 +87,7 @@ class DomainParameters(object):
@public
class DomainParameterCategory(object):
"""A category of domain parameters."""
+
name: str
description: str
curves: List[DomainParameters]
@@ -119,7 +138,9 @@ def _create_params(curve, coords, infty):
param_names = ["a", "d"]
else:
raise ValueError("Unknown curve model.")
- params = {name: Mod(int(curve["params"][name]["raw"], 16), field) for name in param_names}
+ params = {
+ name: Mod(int(curve["params"][name]["raw"], 16), field) for name in param_names
+ }
# Check coordinate model name and assumptions
coord_model: CoordinateModel
@@ -139,8 +160,10 @@ def _create_params(curve, coords, infty):
exec(compiled, None, alocals)
for param, value in alocals.items():
if params[param] != value:
- raise_unsatisified_assumption(getconfig().ec.unsatisfied_coordinate_assumption_action,
- f"Coordinate model {coord_model} has an unsatisifed assumption on the {param} parameter (= {value}).")
+ raise_unsatisified_assumption(
+ getconfig().ec.unsatisfied_coordinate_assumption_action,
+ f"Coordinate model {coord_model} has an unsatisifed assumption on the {param} parameter (= {value}).",
+ )
except NameError:
k = FF(field)
assumption_string = unparse(assumption)
@@ -148,15 +171,23 @@ def _create_params(curve, coords, infty):
expr = sympify(f"{rhs} - {lhs}")
for curve_param, value in params.items():
expr = expr.subs(curve_param, k(value))
- if len(expr.free_symbols) > 1 or (param := str(expr.free_symbols.pop())) not in coord_model.parameters:
- raise ValueError(f"This coordinate model couldn't be loaded due to an unsupported assumption ({assumption_string}).")
+ if (
+ len(expr.free_symbols) > 1
+ or (param := str(expr.free_symbols.pop()))
+ not in coord_model.parameters
+ ):
+ raise ValueError(
+ f"This coordinate model couldn't be loaded due to an unsupported assumption ({assumption_string})."
+ )
poly = Poly(expr, symbols(param), domain=k)
roots = poly.ground_roots()
for root in roots.keys():
params[param] = Mod(int(root), field)
break
else:
- raise UnsatisfiedAssumptionError(f"Coordinate model {coord_model} has an unsatisifed assumption on the {param} parameter (0 = {expr}).")
+ raise UnsatisfiedAssumptionError(
+ f"Coordinate model {coord_model} has an unsatisifed assumption on the {param} parameter (0 = {expr})."
+ )
# Construct the point at infinity
infinity: Point
@@ -170,26 +201,35 @@ def _create_params(curve, coords, infty):
infinity_coords = {}
for coordinate in coord_model.variables:
if coordinate not in ilocals:
- raise ValueError(f"Coordinate model {coord_model} requires infty option.")
+ raise ValueError(
+ f"Coordinate model {coord_model} requires infty option."
+ )
value = ilocals[coordinate]
if isinstance(value, int):
value = Mod(value, field)
infinity_coords[coordinate] = value
infinity = Point(coord_model, **infinity_coords)
elliptic_curve = EllipticCurve(model, coord_model, field, infinity, params) # type: ignore[arg-type]
- affine = Point(AffineCoordinateModel(model),
- x=Mod(int(curve["generator"]["x"]["raw"], 16), field),
- y=Mod(int(curve["generator"]["y"]["raw"], 16), field))
+ affine = Point(
+ AffineCoordinateModel(model),
+ x=Mod(int(curve["generator"]["x"]["raw"], 16), field),
+ y=Mod(int(curve["generator"]["y"]["raw"], 16), field),
+ )
if not isinstance(coord_model, AffineCoordinateModel):
generator = affine.to_model(coord_model, elliptic_curve)
else:
generator = affine
- return DomainParameters(elliptic_curve, generator, order, cofactor, curve["name"], curve["category"])
+ return DomainParameters(
+ elliptic_curve, generator, order, cofactor, curve["name"], curve["category"]
+ )
@public
-def load_category(file: Union[str, Path, BinaryIO], coords: Union[str, Callable[[str], str]],
- infty: Union[bool, Callable[[str], bool]] = True) -> DomainParameterCategory:
+def load_category(
+ file: Union[str, Path, BinaryIO],
+ coords: Union[str, Callable[[str], str]],
+ infty: Union[bool, Callable[[str], bool]] = True,
+) -> DomainParameterCategory:
"""
Load a category of domain parameters containing several curves from a JSON file.
@@ -223,7 +263,9 @@ def load_category(file: Union[str, Path, BinaryIO], coords: Union[str, Callable[
@public
-def load_params(file: Union[str, Path, BinaryIO], coords: str, infty: bool = True) -> DomainParameters:
+def load_params(
+ file: Union[str, Path, BinaryIO], coords: str, infty: bool = True
+) -> DomainParameters:
"""
Load a curve from a JSON file.
@@ -245,8 +287,11 @@ def load_params(file: Union[str, Path, BinaryIO], coords: str, infty: bool = Tru
@public
-def get_category(category: str, coords: Union[str, Callable[[str], str]],
- infty: Union[bool, Callable[[str], bool]] = True) -> DomainParameterCategory:
+def get_category(
+ category: str,
+ coords: Union[str, Callable[[str], str]],
+ infty: Union[bool, Callable[[str], bool]] = True,
+) -> DomainParameterCategory:
"""
Retrieve a category from the std-curves database at https://github.com/J08nY/std-curves.
@@ -259,7 +304,9 @@ def get_category(category: str, coords: Union[str, Callable[[str], str]],
:return: The category.
"""
listing = resource_listdir(__name__, "std")
- categories = list(entry for entry in listing if resource_isdir(__name__, join("std", entry)))
+ categories = list(
+ entry for entry in listing if resource_isdir(__name__, join("std", entry))
+ )
if category not in categories:
raise ValueError(f"Category {category} not found.")
json_path = join("std", category, "curves.json")
@@ -268,7 +315,9 @@ def get_category(category: str, coords: Union[str, Callable[[str], str]],
@public
-def get_params(category: str, name: str, coords: str, infty: bool = True) -> DomainParameters:
+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.
@@ -281,7 +330,9 @@ def get_params(category: str, name: str, coords: str, infty: bool = True) -> Dom
:return: The curve.
"""
listing = resource_listdir(__name__, "std")
- categories = list(entry for entry in listing if resource_isdir(__name__, join("std", entry)))
+ categories = list(
+ entry for entry in listing if resource_isdir(__name__, join("std", entry))
+ )
if category not in categories:
raise ValueError(f"Category {category} not found.")
json_path = join("std", category, "curves.json")
diff --git a/pyecsca/ec/point.py b/pyecsca/ec/point.py
index 6ba6469..1d074fe 100644
--- a/pyecsca/ec/point.py
+++ b/pyecsca/ec/point.py
@@ -19,11 +19,14 @@ if TYPE_CHECKING:
@public
class CoordinateMappingAction(ResultAction):
"""A mapping of a point from one coordinate system to another one, usually one is an affine one."""
+
model_from: CoordinateModel
model_to: CoordinateModel
point: "Point"
- def __init__(self, model_from: CoordinateModel, model_to: CoordinateModel, point: "Point"):
+ def __init__(
+ self, model_from: CoordinateModel, model_to: CoordinateModel, point: "Point"
+ ):
super().__init__()
self.model_from = model_from
self.model_to = model_to
@@ -36,6 +39,7 @@ class CoordinateMappingAction(ResultAction):
@public
class Point(object):
"""A point with coordinates in a coordinate model."""
+
coordinate_model: CoordinateModel
coords: Mapping[str, Mod]
field: int
@@ -51,7 +55,9 @@ class Point(object):
field = value.n
else:
if field != value.n:
- raise ValueError(f"Mismatched coordinate field of definition, {field} vs {value.n}.")
+ raise ValueError(
+ f"Mismatched coordinate field of definition, {field} vs {value.n}."
+ )
self.field = field if field is not None else 0
def __getattribute__(self, name):
@@ -65,7 +71,9 @@ class Point(object):
def to_affine(self) -> "Point":
"""Convert this point into the affine coordinate model, if possible."""
affine_model = AffineCoordinateModel(self.coordinate_model.curve_model)
- with CoordinateMappingAction(self.coordinate_model, affine_model, self) as action:
+ with CoordinateMappingAction(
+ self.coordinate_model, affine_model, self
+ ) as action:
if isinstance(self.coordinate_model, AffineCoordinateModel):
return action.exit(copy(self))
ops = []
@@ -91,11 +99,15 @@ class Point(object):
result[op.result] = locls[op.result]
return action.exit(Point(affine_model, **result))
- def to_model(self, coordinate_model: CoordinateModel, curve: "EllipticCurve") -> "Point":
+ def to_model(
+ self, coordinate_model: CoordinateModel, curve: "EllipticCurve"
+ ) -> "Point":
"""Convert an affine point into a given coordinate model, if possible."""
if not isinstance(self.coordinate_model, AffineCoordinateModel):
raise ValueError
- with CoordinateMappingAction(self.coordinate_model, coordinate_model, self) as action:
+ with CoordinateMappingAction(
+ self.coordinate_model, coordinate_model, self
+ ) as action:
ops = []
for s in coordinate_model.satisfying:
try:
@@ -114,7 +126,10 @@ class Point(object):
result[var] = locls[var]
elif var == "X":
result[var] = self.coords["x"]
- if isinstance(coordinate_model, EFDCoordinateModel) and coordinate_model.name == "inverted":
+ if (
+ isinstance(coordinate_model, EFDCoordinateModel)
+ and coordinate_model.name == "inverted"
+ ):
result[var] = result[var].inverse()
elif var == "Y":
result[var] = self.coords["y"]
@@ -124,11 +139,13 @@ class Point(object):
elif coordinate_model.name == "yz":
result[var] = result[var] * curve.parameters["r"]
elif coordinate_model.name == "yzsquared":
- result[var] = result[var]**2 * curve.parameters["r"]
+ result[var] = result[var] ** 2 * curve.parameters["r"]
elif var.startswith("Z"):
result[var] = Mod(1, curve.prime)
elif var == "T":
- result[var] = Mod(int(self.coords["x"] * self.coords["y"]), curve.prime)
+ result[var] = Mod(
+ int(self.coords["x"] * self.coords["y"]), curve.prime
+ )
else:
raise NotImplementedError
return action.exit(Point(coordinate_model, **result))
@@ -201,7 +218,9 @@ class InfinityPoint(Point):
def to_affine(self) -> "InfinityPoint":
return InfinityPoint(AffineCoordinateModel(self.coordinate_model.curve_model))
- def to_model(self, coordinate_model: CoordinateModel, curve: "EllipticCurve") -> "InfinityPoint":
+ def to_model(
+ self, coordinate_model: CoordinateModel, curve: "EllipticCurve"
+ ) -> "InfinityPoint":
return InfinityPoint(coordinate_model)
def equals_affine(self, other: "Point") -> bool:
diff --git a/pyecsca/ec/signature.py b/pyecsca/ec/signature.py
index 9df6599..0e6f546 100644
--- a/pyecsca/ec/signature.py
+++ b/pyecsca/ec/signature.py
@@ -18,12 +18,20 @@ from .point import Point
@public
class SignatureResult(object):
"""An 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,
+ data: Optional[bytes] = None,
+ digest: Optional[bytes] = None,
+ nonce: Optional[int] = None,
+ privkey: Optional[Mod] = None,
+ pubkey: Optional[Point] = None,
+ ):
self.r = r
self.s = s
@@ -55,12 +63,12 @@ class SignatureResult(object):
@public
class ECDSAAction(Action):
"""An ECDSA action base class."""
+
params: DomainParameters
hash_algo: Optional[Any]
msg: bytes
- def __init__(self, params: DomainParameters, hash_algo: Optional[Any],
- msg: bytes):
+ def __init__(self, params: DomainParameters, hash_algo: Optional[Any], msg: bytes):
super().__init__()
self.params = params
self.hash_algo = hash_algo
@@ -73,10 +81,16 @@ class ECDSAAction(Action):
@public
class ECDSASignAction(ECDSAAction):
"""An ECDSA signing."""
+
privkey: Mod
- def __init__(self, params: DomainParameters, hash_algo: Optional[Any], msg: bytes,
- privkey: Mod):
+ def __init__(
+ self,
+ params: DomainParameters,
+ hash_algo: Optional[Any],
+ msg: bytes,
+ privkey: Mod,
+ ):
super().__init__(params, hash_algo, msg)
self.privkey = privkey
@@ -87,11 +101,18 @@ class ECDSASignAction(ECDSAAction):
@public
class ECDSAVerifyAction(ECDSAAction):
"""An ECDSA verification."""
+
signature: SignatureResult
pubkey: Point
- def __init__(self, params: DomainParameters, hash_algo: Optional[Any], msg: bytes,
- signature: SignatureResult, pubkey: Point):
+ def __init__(
+ self,
+ params: DomainParameters,
+ hash_algo: Optional[Any],
+ msg: bytes,
+ signature: SignatureResult,
+ pubkey: Point,
+ ):
super().__init__(params, hash_algo, msg)
self.signature = signature
self.pubkey = pubkey
@@ -103,6 +124,7 @@ class ECDSAVerifyAction(ECDSAAction):
@public
class Signature(object):
"""An EC based signature primitive. (ECDSA)"""
+
mult: ScalarMultiplier
params: DomainParameters
add: Optional[AdditionFormula]
@@ -110,10 +132,15 @@ class Signature(object):
privkey: Optional[Mod]
hash_algo: Optional[Any]
- def __init__(self, mult: ScalarMultiplier, params: DomainParameters,
- add: Optional[AdditionFormula] = None,
- pubkey: Optional[Point] = None, privkey: Optional[Mod] = None,
- hash_algo: Optional[Any] = None):
+ def __init__(
+ self,
+ mult: ScalarMultiplier,
+ params: DomainParameters,
+ add: Optional[AdditionFormula] = None,
+ pubkey: Optional[Point] = None,
+ privkey: Optional[Mod] = None,
+ hash_algo: Optional[Any] = None,
+ ):
if pubkey is None and privkey is None:
raise ValueError
if add is None:
@@ -153,8 +180,9 @@ 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), digest=digest, nonce=int(nonce), privkey=self.privkey
+ )
def sign_hash(self, digest: bytes, nonce: Optional[int] = None) -> SignatureResult:
"""Sign already hashed data."""
@@ -203,7 +231,9 @@ class Signature(object):
"""Verify data."""
if not self.can_verify or self.pubkey is None:
raise RuntimeError("This instance cannot verify.")
- with ECDSAVerifyAction(self.params, self.hash_algo, data, signature, self.pubkey):
+ with ECDSAVerifyAction(
+ self.params, self.hash_algo, data, signature, self.pubkey
+ ):
if self.hash_algo is None:
digest = data
else:
@@ -215,9 +245,14 @@ class Signature(object):
class ECDSA_NONE(Signature):
"""ECDSA with raw message input."""
- def __init__(self, mult: ScalarMultiplier, params: DomainParameters,
- add: Optional[AdditionFormula] = None,
- pubkey: Optional[Point] = None, privkey: Optional[Mod] = None):
+ def __init__(
+ self,
+ mult: ScalarMultiplier,
+ params: DomainParameters,
+ add: Optional[AdditionFormula] = None,
+ pubkey: Optional[Point] = None,
+ privkey: Optional[Mod] = None,
+ ):
super().__init__(mult, params, add, pubkey, privkey)
@@ -225,9 +260,14 @@ class ECDSA_NONE(Signature):
class ECDSA_SHA1(Signature):
"""ECDSA with SHA1."""
- def __init__(self, mult: ScalarMultiplier, params: DomainParameters,
- add: Optional[AdditionFormula] = None,
- pubkey: Optional[Point] = None, privkey: Optional[Mod] = None):
+ def __init__(
+ self,
+ mult: ScalarMultiplier,
+ params: DomainParameters,
+ add: Optional[AdditionFormula] = None,
+ pubkey: Optional[Point] = None,
+ privkey: Optional[Mod] = None,
+ ):
super().__init__(mult, params, add, pubkey, privkey, hashlib.sha1)
@@ -235,9 +275,14 @@ class ECDSA_SHA1(Signature):
class ECDSA_SHA224(Signature):
"""ECDSA with SHA224."""
- def __init__(self, mult: ScalarMultiplier, params: DomainParameters,
- add: Optional[AdditionFormula] = None,
- pubkey: Optional[Point] = None, privkey: Optional[Mod] = None):
+ def __init__(
+ self,
+ mult: ScalarMultiplier,
+ params: DomainParameters,
+ add: Optional[AdditionFormula] = None,
+ pubkey: Optional[Point] = None,
+ privkey: Optional[Mod] = None,
+ ):
super().__init__(mult, params, add, pubkey, privkey, hashlib.sha224)
@@ -245,9 +290,14 @@ class ECDSA_SHA224(Signature):
class ECDSA_SHA256(Signature):
"""ECDSA with SHA256."""
- def __init__(self, mult: ScalarMultiplier, params: DomainParameters,
- add: Optional[AdditionFormula] = None,
- pubkey: Optional[Point] = None, privkey: Optional[Mod] = None):
+ def __init__(
+ self,
+ mult: ScalarMultiplier,
+ params: DomainParameters,
+ add: Optional[AdditionFormula] = None,
+ pubkey: Optional[Point] = None,
+ privkey: Optional[Mod] = None,
+ ):
super().__init__(mult, params, add, pubkey, privkey, hashlib.sha256)
@@ -255,9 +305,14 @@ class ECDSA_SHA256(Signature):
class ECDSA_SHA384(Signature):
"""ECDSA with SHA384."""
- def __init__(self, mult: ScalarMultiplier, params: DomainParameters,
- add: Optional[AdditionFormula] = None,
- pubkey: Optional[Point] = None, privkey: Optional[Mod] = None):
+ def __init__(
+ self,
+ mult: ScalarMultiplier,
+ params: DomainParameters,
+ add: Optional[AdditionFormula] = None,
+ pubkey: Optional[Point] = None,
+ privkey: Optional[Mod] = None,
+ ):
super().__init__(mult, params, add, pubkey, privkey, hashlib.sha384)
@@ -265,7 +320,12 @@ class ECDSA_SHA384(Signature):
class ECDSA_SHA512(Signature):
"""ECDSA with SHA512."""
- def __init__(self, mult: ScalarMultiplier, params: DomainParameters,
- add: Optional[AdditionFormula] = None,
- pubkey: Optional[Point] = None, privkey: Optional[Mod] = None):
+ def __init__(
+ self,
+ mult: ScalarMultiplier,
+ params: DomainParameters,
+ add: Optional[AdditionFormula] = None,
+ pubkey: Optional[Point] = None,
+ privkey: Optional[Mod] = None,
+ ):
super().__init__(mult, params, add, pubkey, privkey, hashlib.sha512)
diff --git a/pyecsca/ec/transformations.py b/pyecsca/ec/transformations.py
index 986064b..0987235 100644
--- a/pyecsca/ec/transformations.py
+++ b/pyecsca/ec/transformations.py
@@ -22,8 +22,12 @@ def __M_map(params, param_names, map_parameters, map_point, model):
else:
neutral = map_point(param_one, param_other, params.curve.neutral, aff)
curve = EllipticCurve(model, aff, params.curve.prime, neutral, parameters)
- return DomainParameters(curve, map_point(param_one, param_other, params.generator, aff), params.order,
- params.cofactor)
+ return DomainParameters(
+ curve,
+ map_point(param_one, param_other, params.generator, aff),
+ params.order,
+ params.cofactor,
+ )
@public
@@ -35,7 +39,8 @@ def M2SW(params: DomainParameters) -> DomainParameters:
:return: The converted domain parameters.
"""
if not isinstance(params.curve.model, MontgomeryModel) or not isinstance(
- params.curve.coordinate_model, AffineCoordinateModel):
+ params.curve.coordinate_model, AffineCoordinateModel
+ ):
raise ValueError
def map_parameters(A, B):
@@ -48,7 +53,9 @@ def M2SW(params: DomainParameters) -> DomainParameters:
v = pt.y / B
return Point(aff, x=u, y=v)
- return __M_map(params, ("a", "b"), map_parameters, map_point, ShortWeierstrassModel())
+ return __M_map(
+ params, ("a", "b"), map_parameters, map_point, ShortWeierstrassModel()
+ )
@public
@@ -60,7 +67,8 @@ def M2TE(params: DomainParameters) -> DomainParameters:
:return: The converted domain parameters.
"""
if not isinstance(params.curve.model, MontgomeryModel) or not isinstance(
- params.curve.coordinate_model, AffineCoordinateModel):
+ params.curve.coordinate_model, AffineCoordinateModel
+ ):
raise ValueError
def map_parameters(A, B):
@@ -85,7 +93,8 @@ def TE2M(params: DomainParameters) -> DomainParameters:
:return: The converted domain parameters.
"""
if not isinstance(params.curve.model, TwistedEdwardsModel) or not isinstance(
- params.curve.coordinate_model, AffineCoordinateModel):
+ params.curve.coordinate_model, AffineCoordinateModel
+ ):
raise ValueError
def map_parameters(a, d):
@@ -110,16 +119,25 @@ def SW2M(params: DomainParameters) -> DomainParameters:
:return: The converted domain parameters.
"""
if not isinstance(params.curve.model, ShortWeierstrassModel) or not isinstance(
- params.curve.coordinate_model, AffineCoordinateModel):
+ params.curve.coordinate_model, AffineCoordinateModel
+ ):
raise ValueError
ax = symbols("α")
field = FF(params.curve.prime)
- rhs = Poly(ax ** 3 + field(int(params.curve.parameters["a"])) * ax + field(int(params.curve.parameters["b"])), ax, domain=field)
+ rhs = Poly(
+ ax ** 3
+ + field(int(params.curve.parameters["a"])) * ax
+ + field(int(params.curve.parameters["b"])),
+ ax,
+ domain=field,
+ )
roots = rhs.ground_roots()
if not roots:
- raise ValueError("Curve cannot be transformed to Montgomery model (x^3 + ax + b has no root).")
+ raise ValueError(
+ "Curve cannot be transformed to Montgomery model (x^3 + ax + b has no root)."
+ )
alpha = Mod(int(next(iter(roots.keys()))), params.curve.prime)
- beta = (3 * alpha**2 + params.curve.parameters["a"]).sqrt()
+ beta = (3 * alpha ** 2 + params.curve.parameters["a"]).sqrt()
def map_parameters(a, b):
A = (3 * alpha) / beta
@@ -143,16 +161,25 @@ def SW2TE(params: DomainParameters) -> DomainParameters:
:return: The converted domain parameters.
"""
if not isinstance(params.curve.model, ShortWeierstrassModel) or not isinstance(
- params.curve.coordinate_model, AffineCoordinateModel):
+ params.curve.coordinate_model, AffineCoordinateModel
+ ):
raise ValueError
ax = symbols("α")
field = FF(params.curve.prime)
- rhs = Poly(ax ** 3 + field(int(params.curve.parameters["a"])) * ax + field(int(params.curve.parameters["b"])), ax, domain=field)
+ rhs = Poly(
+ ax ** 3
+ + field(int(params.curve.parameters["a"])) * ax
+ + field(int(params.curve.parameters["b"])),
+ ax,
+ domain=field,
+ )
roots = rhs.ground_roots()
if not roots:
- raise ValueError("Curve cannot be transformed to Montgomery model (x^3 + ax + b has no root).")
+ raise ValueError(
+ "Curve cannot be transformed to Montgomery model (x^3 + ax + b has no root)."
+ )
alpha = Mod(int(next(iter(roots.keys()))), params.curve.prime)
- beta = (3 * alpha**2 + params.curve.parameters["a"]).sqrt()
+ beta = (3 * alpha ** 2 + params.curve.parameters["a"]).sqrt()
def map_parameters(a, b):
a = 3 * alpha + 2 * beta
diff --git a/pyecsca/misc/cfg.py b/pyecsca/misc/cfg.py
index df3cce1..8a5a9ee 100644
--- a/pyecsca/misc/cfg.py
+++ b/pyecsca/misc/cfg.py
@@ -11,6 +11,7 @@ from public import public
@public
class ECConfig(object):
"""Configuration for the :py:mod:`pyecsca.ec` package."""
+
_no_inverse_action: str = "error"
_non_residue_action: str = "error"
_unsatisfied_formula_assumption_action: str = "error"
@@ -111,6 +112,7 @@ class ECConfig(object):
@public
class Config(object):
"""A runtime configuration for the library."""
+
ec: ECConfig
"""Configuration for the :py:mod:`pyecsca.ec` package."""
diff --git a/pyecsca/sca/re/rpa.py b/pyecsca/sca/re/rpa.py
index 3dc00e2..ab038e8 100644
--- a/pyecsca/sca/re/rpa.py
+++ b/pyecsca/sca/re/rpa.py
@@ -7,8 +7,15 @@ This module provides functionality inspired by the Refined-Power Analysis attack
from public import public
from typing import MutableMapping, Optional
-from ...ec.formula import FormulaAction, DoublingFormula, AdditionFormula, TriplingFormula, NegationFormula, \
- DifferentialAdditionFormula, LadderFormula
+from ...ec.formula import (
+ FormulaAction,
+ DoublingFormula,
+ AdditionFormula,
+ TriplingFormula,
+ NegationFormula,
+ DifferentialAdditionFormula,
+ LadderFormula,
+)
from ...ec.mult import ScalarMultiplicationAction, PrecomputationAction
from ...ec.point import Point
from ...ec.context import Context, Action
@@ -17,6 +24,7 @@ from ...ec.context import Context, Action
@public
class MultipleContext(Context):
"""A context that traces the multiples of points computed."""
+
base: Optional[Point]
points: MutableMapping[Point, int]
inside: bool
@@ -51,7 +59,7 @@ class MultipleContext(Context):
elif isinstance(action.formula, NegationFormula):
inp = action.input_points[0]
out = action.output_points[0]
- self.points[out] = - self.points[inp]
+ self.points[out] = -self.points[inp]
elif isinstance(action.formula, DifferentialAdditionFormula):
diff, one, other = action.input_points
out = action.output_points[0]
diff --git a/pyecsca/sca/scope/base.py b/pyecsca/sca/scope/base.py
index 68d60dd..595fe32 100644
--- a/pyecsca/sca/scope/base.py
+++ b/pyecsca/sca/scope/base.py
@@ -12,6 +12,7 @@ from ..trace import Trace
@public
class SampleType(Enum):
"""The sample unit."""
+
Raw = auto()
Volt = auto()
@@ -29,7 +30,9 @@ class Scope(object):
"""A list of channels available on this scope."""
raise NotImplementedError
- def setup_frequency(self, frequency: int, pretrig: int, posttrig: int) -> Tuple[int, int]:
+ def setup_frequency(
+ 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
@@ -42,8 +45,9 @@ class Scope(object):
"""
raise NotImplementedError
- def setup_channel(self, channel: str, coupling: str, range: float, offset: float,
- enable: bool) -> None:
+ def setup_channel(
+ self, channel: str, coupling: str, range: float, offset: float, enable: bool
+ ) -> None:
"""
Setup a channel to use the coupling method and measure the given voltage range.
@@ -55,8 +59,15 @@ class Scope(object):
"""
raise NotImplementedError
- def setup_trigger(self, channel: str, threshold: float, direction: str, delay: int,
- timeout: int, enable: bool) -> None:
+ def setup_trigger(
+ self,
+ channel: str,
+ threshold: float,
+ direction: str,
+ delay: int,
+ timeout: int,
+ enable: bool,
+ ) -> 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
diff --git a/pyecsca/sca/scope/chipwhisperer.py b/pyecsca/sca/scope/chipwhisperer.py
index c5c21de..05c8e69 100644
--- a/pyecsca/sca/scope/chipwhisperer.py
+++ b/pyecsca/sca/scope/chipwhisperer.py
@@ -27,18 +27,29 @@ class ChipWhispererScope(Scope): # pragma: no cover
def channels(self) -> Sequence[str]:
return []
- def setup_frequency(self, frequency: int, pretrig: int, posttrig: int) -> Tuple[int, int]:
+ def setup_frequency(
+ self, frequency: int, pretrig: int, posttrig: int
+ ) -> Tuple[int, int]:
if pretrig != 0:
raise ValueError("ChipWhisperer does not support pretrig samples.")
self.scope.clock.clkgen_freq = frequency
self.scope.samples = posttrig
return self.scope.clock.freq_ctr, self.scope.samples
- def setup_channel(self, channel: str, coupling: str, range: float, offset: float, enable: bool) -> None:
+ def setup_channel(
+ self, channel: str, coupling: str, range: float, offset: float, enable: bool
+ ) -> None:
pass
- def setup_trigger(self, channel: str, threshold: float, direction: str, delay: int,
- timeout: int, enable: bool) -> None:
+ def setup_trigger(
+ self,
+ channel: str,
+ threshold: float,
+ direction: str,
+ delay: int,
+ timeout: int,
+ enable: bool,
+ ) -> None:
if enable:
self.triggers.add(channel)
elif channel in self.triggers:
@@ -55,11 +66,16 @@ class ChipWhispererScope(Scope): # pragma: no cover
def capture(self, timeout: Optional[int] = None) -> bool:
return not self.scope.capture()
- def retrieve(self, channel: str, type: SampleType, dtype=np.float16) -> Optional[Trace]:
+ def retrieve(
+ self, channel: str, type: SampleType, dtype=np.float16
+ ) -> Optional[Trace]:
data = self.scope.get_last_trace()
if data is None:
return None
- return Trace(np.array(data, dtype=dtype), {"sampling_frequency": self.scope.clock.clkgen_freq, "channel": channel})
+ return Trace(
+ np.array(data, dtype=dtype),
+ {"sampling_frequency": self.scope.clock.clkgen_freq, "channel": channel},
+ )
def stop(self) -> None:
pass
diff --git a/pyecsca/sca/scope/picoscope_alt.py b/pyecsca/sca/scope/picoscope_alt.py
index 544ef35..577c7dc 100644
--- a/pyecsca/sca/scope/picoscope_alt.py
+++ b/pyecsca/sca/scope/picoscope_alt.py
@@ -39,20 +39,31 @@ class PicoScopeAlt(Scope): # pragma: no cover
def channels(self) -> Sequence[str]:
return list(self.ps.CHANNELS.keys())
- def setup_frequency(self, frequency: int, pretrig: int, posttrig: int) -> Tuple[int, int]:
+ def setup_frequency(
+ self, frequency: int, pretrig: int, posttrig: int
+ ) -> Tuple[int, int]:
samples = pretrig + posttrig
actual_frequency, max_samples = self.ps.setSamplingFrequency(frequency, samples)
if max_samples < samples:
- self.trig_ratio = (pretrig / samples)
+ self.trig_ratio = pretrig / samples
samples = max_samples
self.frequency = actual_frequency
return actual_frequency, samples
- def setup_channel(self, channel: str, coupling: str, range: float, offset: float, enable: bool) -> None:
+ def setup_channel(
+ self, channel: str, coupling: str, range: float, offset: float, enable: bool
+ ) -> None:
self.ps.setChannel(channel, coupling, range, offset, enable)
- def setup_trigger(self, channel: str, threshold: float, direction: str, delay: int,
- timeout: int, enable: bool) -> None:
+ def setup_trigger(
+ self,
+ channel: str,
+ threshold: float,
+ direction: str,
+ delay: int,
+ timeout: int,
+ enable: bool,
+ ) -> None:
self.ps.setSimpleTrigger(channel, threshold, direction, delay, timeout, enable)
def setup_capture(self, channel: str, enable: bool) -> None:
@@ -69,14 +80,23 @@ class PicoScopeAlt(Scope): # pragma: no cover
return False
return True
- def retrieve(self, channel: str, type: SampleType, dtype=np.float32) -> Optional[Trace]:
+ def retrieve(
+ self, channel: str, type: SampleType, dtype=np.float32
+ ) -> Optional[Trace]:
if type == SampleType.Raw:
data = self.ps.getDataRaw(channel).astype(dtype=dtype, copy=False)
else:
data = self.ps.getDataV(channel, dtype=dtype)
if data is None:
return None
- return Trace(data, {"sampling_frequency": self.frequency, "channel": channel, "sample_type": type})
+ return Trace(
+ data,
+ {
+ "sampling_frequency": self.frequency,
+ "channel": channel,
+ "sample_type": type,
+ },
+ )
def stop(self) -> None:
self.ps.stop()
diff --git a/pyecsca/sca/scope/picoscope_sdk.py b/pyecsca/sca/scope/picoscope_sdk.py
index 8101f94..6237bb8 100644
--- a/pyecsca/sca/scope/picoscope_sdk.py
+++ b/pyecsca/sca/scope/picoscope_sdk.py
@@ -11,6 +11,7 @@ import numpy as np
from picosdk.errors import CannotFindPicoSDKError
from picosdk.functions import assert_pico_ok
from picosdk.library import Library
+
try:
from picosdk.ps3000 import ps3000
except CannotFindPicoSDKError as exc:
@@ -33,8 +34,12 @@ from .base import Scope, SampleType
from ..trace import Trace
-def adc2volt(adc: Union[np.ndarray, ctypes.c_int16],
- volt_range: float, adc_minmax: int, dtype=np.float32) -> Union[np.ndarray, float]: # pragma: no cover
+def adc2volt(
+ adc: Union[np.ndarray, ctypes.c_int16],
+ volt_range: float,
+ adc_minmax: int,
+ dtype=np.float32,
+) -> Union[np.ndarray, float]: # pragma: no cover
"""
Convert raw adc values to volts.
@@ -51,8 +56,9 @@ def adc2volt(adc: Union[np.ndarray, ctypes.c_int16],
raise ValueError
-def volt2adc(volt: Union[np.ndarray, float],
- volt_range: float, adc_minmax: int, dtype=np.float32) -> Union[np.ndarray, ctypes.c_int16]: # pragma: no cover
+def volt2adc(
+ volt: Union[np.ndarray, float], volt_range: float, adc_minmax: int, dtype=np.float32
+) -> Union[np.ndarray, ctypes.c_int16]: # pragma: no cover
"""
Convert volt values to raw adc values.
@@ -72,6 +78,7 @@ def volt2adc(volt: Union[np.ndarray, float],
@public
class PicoScopeSdk(Scope): # pragma: no cover
"""A PicoScope based scope."""
+
MODULE: Library
PREFIX: str
CHANNELS: Mapping
@@ -80,12 +87,7 @@ class PicoScopeSdk(Scope): # pragma: no cover
MIN_ADC_VALUE: int
COUPLING: Mapping
TIME_UNITS: Mapping
- TRIGGERS: Mapping = {
- "above": 0,
- "below": 1,
- "rising": 2,
- "falling": 3
- }
+ TRIGGERS: Mapping = {"above": 0, "below": 1, "rising": 2, "falling": 3}
_variant: Optional[str]
def __init__(self, variant: Optional[str] = None):
@@ -112,28 +114,52 @@ class PicoScopeSdk(Scope): # pragma: no cover
return self._variant
info = (ctypes.c_int8 * 6)()
size = ctypes.c_int16()
- assert_pico_ok(self.__dispatch_call("GetUnitInfo", self.handle, ctypes.byref(info), 6,
- ctypes.byref(size), 3))
- self._variant = "".join(chr(i) for i in info[:size.value])
+ assert_pico_ok(
+ self.__dispatch_call(
+ "GetUnitInfo", self.handle, ctypes.byref(info), 6, ctypes.byref(size), 3
+ )
+ )
+ self._variant = "".join(chr(i) for i in info[: size.value])
return self._variant
- def setup_frequency(self, frequency: int, pretrig: int, posttrig: int) -> Tuple[int, int]:
+ def setup_frequency(
+ self, frequency: int, pretrig: int, posttrig: int
+ ) -> Tuple[int, int]:
return self.set_frequency(frequency, pretrig, posttrig)
- def set_channel(self, channel: str, enabled: bool, coupling: str, range: float, offset: float):
+ def set_channel(
+ self, channel: str, enabled: bool, coupling: str, range: float, offset: float
+ ):
if offset != 0.0:
raise ValueError("Nonzero offset not supported.")
assert_pico_ok(
- self.__dispatch_call("SetChannel", self.handle, self.CHANNELS[channel], enabled,
- self.COUPLING[coupling], self.RANGES[range]))
+ self.__dispatch_call(
+ "SetChannel",
+ self.handle,
+ self.CHANNELS[channel],
+ enabled,
+ self.COUPLING[coupling],
+ self.RANGES[range],
+ )
+ )
self.ranges[channel] = range
- def setup_channel(self, channel: str, coupling: str, range: float, offset: float, enable: bool):
+ def setup_channel(
+ self, channel: str, coupling: str, range: float, offset: float, enable: bool
+ ):
self.set_channel(channel, enable, coupling, range, offset)
- def _set_freq(self, frequency: int, pretrig: int, posttrig: int, period_bound: float,
- timebase_bound: int,
- low_freq: int, high_freq: int, high_subtract: int) -> Tuple[int, int]:
+ def _set_freq(
+ self,
+ frequency: int,
+ pretrig: int,
+ posttrig: int,
+ period_bound: float,
+ timebase_bound: int,
+ low_freq: int,
+ high_freq: int,
+ high_subtract: int,
+ ) -> Tuple[int, int]:
samples = pretrig + posttrig
period = 1 / frequency
if low_freq == 0 or period > period_bound:
@@ -145,8 +171,18 @@ class PicoScopeSdk(Scope): # pragma: no cover
tb = timebase_bound
actual_frequency = low_freq // 2 ** tb
max_samples = ctypes.c_int32()
- assert_pico_ok(self.__dispatch_call("GetTimebase", self.handle, tb, samples, None, 0,
- ctypes.byref(max_samples), 0))
+ assert_pico_ok(
+ self.__dispatch_call(
+ "GetTimebase",
+ self.handle,
+ tb,
+ samples,
+ None,
+ 0,
+ ctypes.byref(max_samples),
+ 0,
+ )
+ )
if max_samples.value < samples:
pretrig = max_samples.value * (pretrig // samples)
posttrig = max_samples.value - pretrig
@@ -158,20 +194,43 @@ class PicoScopeSdk(Scope): # pragma: no cover
self.timebase = tb
return actual_frequency, samples
- def set_frequency(self, frequency: int, pretrig: int, posttrig: int) -> Tuple[int, int]:
+ def set_frequency(
+ self, frequency: int, pretrig: int, posttrig: int
+ ) -> Tuple[int, int]:
raise NotImplementedError
- def setup_trigger(self, channel: str, threshold: float, direction: str, delay: int,
- timeout: int, enable: bool):
+ def setup_trigger(
+ self,
+ channel: str,
+ threshold: float,
+ direction: str,
+ delay: int,
+ timeout: int,
+ enable: bool,
+ ):
self.set_trigger(direction, enable, threshold, channel, delay, timeout)
- def set_trigger(self, type: str, enabled: bool, value: float, channel: str,
- delay: int, timeout: int):
+ def set_trigger(
+ self,
+ type: str,
+ enabled: bool,
+ value: float,
+ channel: str,
+ delay: int,
+ timeout: int,
+ ):
assert_pico_ok(
- self.__dispatch_call("SetSimpleTrigger", self.handle, enabled,
- self.CHANNELS[channel],
- volt2adc(value, self.ranges[channel], self.MAX_ADC_VALUE),
- self.TRIGGERS[type], delay, timeout))
+ self.__dispatch_call(
+ "SetSimpleTrigger",
+ self.handle,
+ enabled,
+ self.CHANNELS[channel],
+ volt2adc(value, self.ranges[channel], self.MAX_ADC_VALUE),
+ self.TRIGGERS[type],
+ delay,
+ timeout,
+ )
+ )
def setup_capture(self, channel: str, enable: bool):
self.set_buffer(channel, enable)
@@ -184,22 +243,44 @@ class PicoScopeSdk(Scope): # pragma: no cover
del self.buffers[channel]
buffer = (ctypes.c_int16 * self.samples)()
assert_pico_ok(
- self.__dispatch_call("SetDataBuffer", self.handle, self.CHANNELS[channel],
- ctypes.byref(buffer), self.samples))
+ self.__dispatch_call(
+ "SetDataBuffer",
+ self.handle,
+ self.CHANNELS[channel],
+ ctypes.byref(buffer),
+ self.samples,
+ )
+ )
self.buffers[channel] = buffer
else:
assert_pico_ok(
- self.__dispatch_call("SetDataBuffer", self.handle, self.CHANNELS[channel],
- None, self.samples))
+ self.__dispatch_call(
+ "SetDataBuffer",
+ self.handle,
+ self.CHANNELS[channel],
+ None,
+ self.samples,
+ )
+ )
del self.buffers[channel]
def arm(self):
if self.samples is None or self.timebase is None:
raise ValueError
assert_pico_ok(
- self.__dispatch_call("RunBlock", self.handle, self.pretrig, self.posttrig,
- self.timebase, 0,
- None, 0, None, None))
+ self.__dispatch_call(
+ "RunBlock",
+ self.handle,
+ self.pretrig,
+ self.posttrig,
+ self.timebase,
+ 0,
+ None,
+ 0,
+ None,
+ None,
+ )
+ )
def capture(self, timeout: Optional[int] = None) -> bool:
start = time_ns()
@@ -209,25 +290,48 @@ class PicoScopeSdk(Scope): # pragma: no cover
check = ctypes.c_int16(0)
while ready.value == check.value:
sleep(0.001)
- assert_pico_ok(self.__dispatch_call("IsReady", self.handle, ctypes.byref(ready)))
+ assert_pico_ok(
+ self.__dispatch_call("IsReady", self.handle, ctypes.byref(ready))
+ )
if timeout is not None and (time_ns() - start) / 1e6 >= timeout:
return False
return True
- def retrieve(self, channel: str, type: SampleType, dtype=np.float32) -> Optional[Trace]:
+ def retrieve(
+ self, channel: str, type: SampleType, dtype=np.float32
+ ) -> Optional[Trace]:
if self.samples is None:
raise ValueError
actual_samples = ctypes.c_int32(self.samples)
overflow = ctypes.c_int16()
assert_pico_ok(
- self.__dispatch_call("GetValues", self.handle, 0, ctypes.byref(actual_samples), 1,
- 0, 0, ctypes.byref(overflow)))
+ self.__dispatch_call(
+ "GetValues",
+ self.handle,
+ 0,
+ ctypes.byref(actual_samples),
+ 1,
+ 0,
+ 0,
+ ctypes.byref(overflow),
+ )
+ )
arr = np.array(self.buffers[channel], dtype=dtype)
if type == SampleType.Raw:
data = arr
else:
- data = cast(np.ndarray, adc2volt(arr, self.ranges[channel], self.MAX_ADC_VALUE, dtype=dtype))
- return Trace(data, {"sampling_frequency": self.frequency, "channel": channel, "sample_type": type})
+ data = cast(
+ np.ndarray,
+ adc2volt(arr, self.ranges[channel], self.MAX_ADC_VALUE, dtype=dtype),
+ )
+ return Trace(
+ data,
+ {
+ "sampling_frequency": self.frequency,
+ "channel": channel,
+ "sample_type": type,
+ },
+ )
def stop(self):
assert_pico_ok(self.__dispatch_call("Stop"))
@@ -243,23 +347,29 @@ class PicoScopeSdk(Scope): # pragma: no cover
if isinstance(ps3000, CannotFindPicoSDKError):
+
@public
class PS3000Scope(PicoScopeSdk): # pragma: no cover
"""A PicoScope 3000 series oscilloscope is not available. (Install `libps3000`)."""
+
def __init__(self, variant: Optional[str] = None):
super().__init__(variant)
raise ps3000
+
+
else: # pragma: no cover
+
@public
class PS3000Scope(PicoScopeSdk): # type: ignore
"""A PicoScope 3000 series oscilloscope."""
+
MODULE = ps3000
PREFIX = "ps3000"
CHANNELS = {
"A": ps3000.PS3000_CHANNEL["PS3000_CHANNEL_A"],
"B": ps3000.PS3000_CHANNEL["PS3000_CHANNEL_B"],
"C": ps3000.PS3000_CHANNEL["PS3000_CHANNEL_C"],
- "D": ps3000.PS3000_CHANNEL["PS3000_CHANNEL_D"]
+ "D": ps3000.PS3000_CHANNEL["PS3000_CHANNEL_D"],
}
RANGES = {
@@ -276,48 +386,57 @@ else: # pragma: no cover
50.0: ps3000.PS3000_VOLTAGE_RANGE["PS3000_50V"],
100.0: ps3000.PS3000_VOLTAGE_RANGE["PS3000_100V"],
200.0: ps3000.PS3000_VOLTAGE_RANGE["PS3000_200V"],
- 400.0: ps3000.PS3000_VOLTAGE_RANGE["PS3000_400V"]
+ 400.0: ps3000.PS3000_VOLTAGE_RANGE["PS3000_400V"],
}
MAX_ADC_VALUE = 32767
MIN_ADC_VALUE = -32767
- COUPLING = {
- "AC": ps3000.PICO_COUPLING["AC"],
- "DC": ps3000.PICO_COUPLING["DC"]
- }
+ COUPLING = {"AC": ps3000.PICO_COUPLING["AC"], "DC": ps3000.PICO_COUPLING["DC"]}
def get_variant(self):
if self._variant is not None:
return self._variant
info = (ctypes.c_int8 * 6)()
size = ctypes.c_int16(6)
- assert_pico_ok(self.__dispatch_call("GetUnitInfo", self.handle, ctypes.byref(info), size, 3))
- self._variant = "".join(chr(i) for i in info[:size.value])
+ assert_pico_ok(
+ self.__dispatch_call(
+ "GetUnitInfo", self.handle, ctypes.byref(info), size, 3
+ )
+ )
+ self._variant = "".join(chr(i) for i in info[: size.value])
return self._variant
- def set_frequency(self, frequency: int, pretrig: int, posttrig: int): # TODO: fix
+ def set_frequency(
+ self, frequency: int, pretrig: int, posttrig: int
+ ): # TODO: fix
raise NotImplementedError
if isinstance(ps4000, CannotFindPicoSDKError):
+
@public
class PS4000Scope(PicoScopeSdk): # pragma: no cover
"""A PicoScope 4000 series oscilloscope is not available. (Install `libps4000`)."""
+
def __init__(self, variant: Optional[str] = None):
super().__init__(variant)
raise ps4000
+
+
else: # pragma: no cover
+
@public
class PS4000Scope(PicoScopeSdk): # type: ignore
"""A PicoScope 4000 series oscilloscope."""
+
MODULE = ps4000
PREFIX = "ps4000"
CHANNELS = {
"A": ps4000.PS4000_CHANNEL["PS4000_CHANNEL_A"],
"B": ps4000.PS4000_CHANNEL["PS4000_CHANNEL_B"],
"C": ps4000.PS4000_CHANNEL["PS4000_CHANNEL_C"],
- "D": ps4000.PS4000_CHANNEL["PS4000_CHANNEL_D"]
+ "D": ps4000.PS4000_CHANNEL["PS4000_CHANNEL_D"],
}
RANGES = {
@@ -333,46 +452,54 @@ else: # pragma: no cover
10.0: ps4000.PS4000_RANGE["PS4000_10V"],
20.0: ps4000.PS4000_RANGE["PS4000_20V"],
50.0: ps4000.PS4000_RANGE["PS4000_50V"],
- 100.0: ps4000.PS4000_RANGE["PS4000_100V"]
+ 100.0: ps4000.PS4000_RANGE["PS4000_100V"],
}
MAX_ADC_VALUE = 32764
MIN_ADC_VALUE = -32764
- COUPLING = {
- "AC": ps4000.PICO_COUPLING["AC"],
- "DC": ps4000.PICO_COUPLING["DC"]
- }
+ COUPLING = {"AC": ps4000.PICO_COUPLING["AC"], "DC": ps4000.PICO_COUPLING["DC"]}
def set_frequency(self, frequency: int, pretrig: int, posttrig: int):
variant = self.get_variant()
if variant in ("4223", "4224", "4423", "4424"):
- return self._set_freq(frequency, pretrig, posttrig, 50e-9, 2, 80_000_000, 20_000_000, 1)
+ return self._set_freq(
+ frequency, pretrig, posttrig, 50e-9, 2, 80_000_000, 20_000_000, 1
+ )
elif variant in ("4226", "4227"):
- return self._set_freq(frequency, pretrig, posttrig, 32e-9, 3, 250_000_000, 31_250_000,
- 2)
+ return self._set_freq(
+ frequency, pretrig, posttrig, 32e-9, 3, 250_000_000, 31_250_000, 2
+ )
elif variant == "4262":
- return self._set_freq(frequency, pretrig, posttrig, 0, 0, 0, 10_000_000, -1)
+ return self._set_freq(
+ frequency, pretrig, posttrig, 0, 0, 0, 10_000_000, -1
+ )
if isinstance(ps5000, CannotFindPicoSDKError):
+
@public
class PS5000Scope(PicoScopeSdk): # pragma: no cover
"""A PicoScope 5000 series oscilloscope is not available. (Install `libps5000`)."""
+
def __init__(self, variant: Optional[str] = None):
super().__init__(variant)
raise ps5000
+
+
else: # pragma: no cover
+
@public
class PS5000Scope(PicoScopeSdk): # type: ignore
"""A PicoScope 5000 series oscilloscope."""
+
MODULE = ps5000
PREFIX = "ps5000"
CHANNELS = {
"A": ps5000.PS5000_CHANNEL["PS5000_CHANNEL_A"],
"B": ps5000.PS5000_CHANNEL["PS5000_CHANNEL_B"],
"C": ps5000.PS5000_CHANNEL["PS5000_CHANNEL_C"],
- "D": ps5000.PS5000_CHANNEL["PS5000_CHANNEL_D"]
+ "D": ps5000.PS5000_CHANNEL["PS5000_CHANNEL_D"],
}
RANGES = {
@@ -387,39 +514,44 @@ else: # pragma: no cover
5.00: 8,
10.0: 9,
20.0: 10,
- 50.0: 11
+ 50.0: 11,
}
MAX_ADC_VALUE = 32512
MIN_ADC_VALUE = -32512
- COUPLING = {
- "AC": 0,
- "DC": 1
- }
+ COUPLING = {"AC": 0, "DC": 1}
def set_frequency(self, frequency: int, pretrig: int, posttrig: int):
- return self._set_freq(frequency, pretrig, posttrig, 4e-9, 2, 1_000_000_000, 125_000_000, 2)
+ return self._set_freq(
+ frequency, pretrig, posttrig, 4e-9, 2, 1_000_000_000, 125_000_000, 2
+ )
if isinstance(ps6000, CannotFindPicoSDKError):
+
@public
class PS6000Scope(PicoScopeSdk): # pragma: no cover
"""A PicoScope 6000 series oscilloscope is not available. (Install `libps6000`)."""
+
def __init__(self, variant: Optional[str] = None):
super().__init__(variant)
raise ps6000
+
+
else: # pragma: no cover
+
@public
class PS6000Scope(PicoScopeSdk): # type: ignore
"""A PicoScope 6000 series oscilloscope."""
+
MODULE = ps6000
PREFIX = "ps6000"
CHANNELS = {
"A": ps6000.PS6000_CHANNEL["PS6000_CHANNEL_A"],
"B": ps6000.PS6000_CHANNEL["PS6000_CHANNEL_B"],
"C": ps6000.PS6000_CHANNEL["PS6000_CHANNEL_C"],
- "D": ps6000.PS6000_CHANNEL["PS6000_CHANNEL_D"]
+ "D": ps6000.PS6000_CHANNEL["PS6000_CHANNEL_D"],
}
RANGES = {
@@ -434,7 +566,7 @@ else: # pragma: no cover
5.00: ps6000.PS6000_RANGE["PS6000_5V"],
10.0: ps6000.PS6000_RANGE["PS6000_10V"],
20.0: ps6000.PS6000_RANGE["PS6000_20V"],
- 50.0: ps6000.PS6000_RANGE["PS6000_50V"]
+ 50.0: ps6000.PS6000_RANGE["PS6000_50V"],
}
MAX_ADC_VALUE = 32512
@@ -443,16 +575,31 @@ else: # pragma: no cover
COUPLING = {
"AC": ps6000.PS6000_COUPLING["PS6000_AC"],
"DC": ps6000.PS6000_COUPLING["PS6000_DC_1M"],
- "DC_50": ps6000.PS6000_COUPLING["PS6000_DC_50R"]
+ "DC_50": ps6000.PS6000_COUPLING["PS6000_DC_50R"],
}
def open(self):
assert_pico_ok(ps6000.ps6000OpenUnit(ctypes.byref(self.handle), None))
- def set_channel(self, channel: str, enabled: bool, coupling: str, range: float, offset: float):
- assert_pico_ok(ps6000.ps6000SetChannel(self.handle, self.CHANNELS[channel], enabled,
- self.COUPLING[coupling], self.RANGES[range], offset,
- ps6000.PS6000_BANDWIDTH_LIMITER["PS6000_BW_FULL"]))
+ def set_channel(
+ self,
+ channel: str,
+ enabled: bool,
+ coupling: str,
+ range: float,
+ offset: float,
+ ):
+ assert_pico_ok(
+ ps6000.ps6000SetChannel(
+ self.handle,
+ self.CHANNELS[channel],
+ enabled,
+ self.COUPLING[coupling],
+ self.RANGES[range],
+ offset,
+ ps6000.PS6000_BANDWIDTH_LIMITER["PS6000_BW_FULL"],
+ )
+ )
def set_buffer(self, channel: str, enable: bool):
if self.samples is None:
@@ -462,15 +609,24 @@ else: # pragma: no cover
del self.buffers[channel]
buffer = (ctypes.c_int16 * self.samples)()
assert_pico_ok(
- ps6000.ps6000SetDataBuffer(self.handle, self.CHANNELS[channel],
- ctypes.byref(buffer), self.samples, 0))
+ ps6000.ps6000SetDataBuffer(
+ self.handle,
+ self.CHANNELS[channel],
+ ctypes.byref(buffer),
+ self.samples,
+ 0,
+ )
+ )
self.buffers[channel] = buffer
else:
assert_pico_ok(
- ps6000.ps6000SetDataBuffer(self.handle, self.CHANNELS[channel],
- None, self.samples, 0))
+ ps6000.ps6000SetDataBuffer(
+ self.handle, self.CHANNELS[channel], None, self.samples, 0
+ )
+ )
del self.buffers[channel]
def set_frequency(self, frequency: int, pretrig: int, posttrig: int):
- return self._set_freq(frequency, pretrig, posttrig, 3.2e-9, 4, 5_000_000_000, 156_250_000,
- 4)
+ return self._set_freq(
+ frequency, pretrig, posttrig, 3.2e-9, 4, 5_000_000_000, 156_250_000, 4
+ )
diff --git a/pyecsca/sca/target/ISO7816.py b/pyecsca/sca/target/ISO7816.py
index 233685e..1a9c8b8 100644
--- a/pyecsca/sca/target/ISO7816.py
+++ b/pyecsca/sca/target/ISO7816.py
@@ -14,6 +14,7 @@ from .base import Target
@dataclass
class CommandAPDU(object): # pragma: no cover
"""A command APDU that can be sent to an ISO7816-4 target."""
+
cls: int
ins: int
p1: int
@@ -28,30 +29,61 @@ class CommandAPDU(object): # pragma: no cover
return bytes([self.cls, self.ins, self.p1, self.p2])
elif self.ne <= 256:
# Case 2s
- return bytes([self.cls, self.ins, self.p1, self.p2, self.ne if self.ne != 256 else 0])
+ return bytes(
+ [
+ self.cls,
+ self.ins,
+ self.p1,
+ self.p2,
+ self.ne if self.ne != 256 else 0,
+ ]
+ )
else:
# Case 2e
- return bytes([self.cls, self.ins, self.p1, self.p2]) + (self.ne.to_bytes(2, "big") if self.ne != 65536 else bytes([0, 0]))
+ return bytes([self.cls, self.ins, self.p1, self.p2]) + (
+ self.ne.to_bytes(2, "big") if self.ne != 65536 else bytes([0, 0])
+ )
elif self.ne is None or self.ne == 0:
if len(self.data) <= 255:
# Case 3s
- return bytes([self.cls, self.ins, self.p1, self.p2, len(self.data)]) + self.data
+ return (
+ bytes([self.cls, self.ins, self.p1, self.p2, len(self.data)])
+ + self.data
+ )
else:
# Case 3e
- return bytes([self.cls, self.ins, self.p1, self.p2, 0]) + len(self.data).to_bytes(2, "big") + self.data
+ return (
+ bytes([self.cls, self.ins, self.p1, self.p2, 0])
+ + len(self.data).to_bytes(2, "big")
+ + self.data
+ )
else:
if len(self.data) <= 255 and self.ne <= 256:
# Case 4s
- return bytes([self.cls, self.ins, self.p1, self.p2, len(self.data)]) + self.data + bytes([self.ne if self.ne != 256 else 0])
+ return (
+ bytes([self.cls, self.ins, self.p1, self.p2, len(self.data)])
+ + self.data
+ + bytes([self.ne if self.ne != 256 else 0])
+ )
else:
# Case 4e
- return bytes([self.cls, self.ins, self.p1, self.p2, 0]) + len(self.data).to_bytes(2, "big") + self.data + (self.ne.to_bytes(2, "big") if self.ne != 65536 else bytes([0, 0]))
+ return (
+ bytes([self.cls, self.ins, self.p1, self.p2, 0])
+ + len(self.data).to_bytes(2, "big")
+ + self.data
+ + (
+ self.ne.to_bytes(2, "big")
+ if self.ne != 65536
+ else bytes([0, 0])
+ )
+ )
@public
@dataclass
class ResponseAPDU(object):
"""A response APDU that can be received from an ISO7816-4 target."""
+
data: bytes
sw: int
@@ -90,6 +122,7 @@ class ISO7816Target(Target, ABC):
@public
class ISO7816:
"""A bunch of ISO7816-4 constants (status words)."""
+
SW_FILE_FULL = 0x6A84
SW_UNKNOWN = 0x6F00
SW_CLA_NOT_SUPPORTED = 0x6E00
diff --git a/pyecsca/sca/target/PCSC.py b/pyecsca/sca/target/PCSC.py
index a377751..4b2620c 100644
--- a/pyecsca/sca/target/PCSC.py
+++ b/pyecsca/sca/target/PCSC.py
@@ -37,7 +37,7 @@ class PCSCTarget(ISO7816Target): # pragma: no cover
return bytes(self.connection.getATR())
def select(self, aid: bytes) -> bool:
- apdu = CommandAPDU(0x00, 0xa4, 0x04, 0x00, aid)
+ apdu = CommandAPDU(0x00, 0xA4, 0x04, 0x00, aid)
resp = self.send_apdu(apdu)
return resp.sw == ISO7816.SW_NO_ERROR
diff --git a/pyecsca/sca/target/binary.py b/pyecsca/sca/target/binary.py
index 3e2877b..d4c402e 100644
--- a/pyecsca/sca/target/binary.py
+++ b/pyecsca/sca/target/binary.py
@@ -13,11 +13,14 @@ 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: List[str]
process: Optional[Popen] = None
debug_output: bool
- def __init__(self, binary: Union[str, List[str]], debug_output: bool = False, **kwargs):
+ def __init__(
+ self, binary: Union[str, List[str]], debug_output: bool = False, **kwargs
+ ):
super().__init__()
if not isinstance(binary, (str, list)):
raise TypeError
@@ -27,8 +30,13 @@ class BinaryTarget(SerialTarget):
self.debug_output = debug_output
def connect(self):
- self.process = Popen(self.binary, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
- text=True, bufsize=1)
+ self.process = Popen(
+ self.binary,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ text=True,
+ bufsize=1,
+ )
def write(self, data: bytes) -> None:
if self.process is None:
diff --git a/pyecsca/sca/target/chipwhisperer.py b/pyecsca/sca/target/chipwhisperer.py
index ac3fa42..4037f12 100644
--- a/pyecsca/sca/target/chipwhisperer.py
+++ b/pyecsca/sca/target/chipwhisperer.py
@@ -22,7 +22,9 @@ class ChipWhispererTarget(Flashable, SimpleSerialTarget): # pragma: no cover
using ChipWhisperer-Lite/Pro.
"""
- def __init__(self, target: SimpleSerial, scope: ScopeTemplate, programmer, **kwargs):
+ def __init__(
+ self, target: SimpleSerial, scope: ScopeTemplate, programmer, **kwargs
+ ):
super().__init__()
self.target = target
self.scope = scope
diff --git a/pyecsca/sca/target/ectester.py b/pyecsca/sca/target/ectester.py
index 6ab10be..f6635c3 100644
--- a/pyecsca/sca/target/ectester.py
+++ b/pyecsca/sca/target/ectester.py
@@ -49,6 +49,7 @@ class ShiftableFlag(IntFlag): # pragma: no cover
@public
class KeypairEnum(ShiftableFlag): # pragma: no cover
"""ECTester's KeyPair type."""
+
KEYPAIR_LOCAL = 0x01
KEYPAIR_REMOTE = 0x02
KEYPAIR_BOTH = KEYPAIR_LOCAL | KEYPAIR_REMOTE
@@ -57,12 +58,13 @@ class KeypairEnum(ShiftableFlag): # pragma: no cover
@public
class InstructionEnum(IntEnum): # pragma: no cover
"""ECTester's instruction (INS)."""
- INS_ALLOCATE = 0x5a
- INS_CLEAR = 0x5b
- INS_SET = 0x5c
- INS_TRANSFORM = 0x5d
- INS_GENERATE = 0x5e
- INS_EXPORT = 0x5f
+
+ INS_ALLOCATE = 0x5A
+ INS_CLEAR = 0x5B
+ INS_SET = 0x5C
+ INS_TRANSFORM = 0x5D
+ INS_GENERATE = 0x5E
+ INS_EXPORT = 0x5F
INS_ECDH = 0x70
INS_ECDH_DIRECT = 0x71
INS_ECDSA = 0x72
@@ -73,13 +75,14 @@ class InstructionEnum(IntEnum): # pragma: no cover
INS_ALLOCATE_SIG = 0x77
INS_GET_INFO = 0x78
INS_SET_DRY_RUN_MODE = 0x79
- INS_BUFFER = 0x7a
- INS_PERFORM = 0x7b
+ INS_BUFFER = 0x7A
+ INS_PERFORM = 0x7B
@public
class KeyBuildEnum(IntEnum): # pragma: no cover
"""ECTester's key builder type."""
+
BUILD_KEYPAIR = 0x01
BUILD_KEYBUILDER = 0x02
@@ -87,7 +90,8 @@ class KeyBuildEnum(IntEnum): # pragma: no cover
@public
class ExportEnum(IntEnum): # pragma: no cover
"""ECTester's export boolean."""
- EXPORT_TRUE = 0xff
+
+ EXPORT_TRUE = 0xFF
EXPORT_FALSE = 0x00
@classmethod
@@ -98,13 +102,15 @@ class ExportEnum(IntEnum): # pragma: no cover
@public
class RunModeEnum(IntEnum): # pragma: no cover
"""ECTester's run mode."""
- MODE_NORMAL = 0xaa
- MODE_DRY_RUN = 0xbb
+
+ MODE_NORMAL = 0xAA
+ MODE_DRY_RUN = 0xBB
@public
class KeyEnum(ShiftableFlag): # pragma: no cover
"""ECTester's key enum."""
+
PUBLIC = 0x01
PRIVATE = 0x02
BOTH = PRIVATE | PUBLIC
@@ -113,6 +119,7 @@ class KeyEnum(ShiftableFlag): # pragma: no cover
@public
class AppletBaseEnum(IntEnum): # pragma: no cover
"""ECTester's JavaCard applet base version."""
+
BASE_221 = 0x0221
BASE_222 = 0x0222
@@ -120,6 +127,7 @@ class AppletBaseEnum(IntEnum): # pragma: no cover
@public
class KeyClassEnum(IntEnum): # pragma: no cover
"""JavaCard EC-based key class."""
+
ALG_EC_F2M = 4
ALG_EC_FP = 5
@@ -127,6 +135,7 @@ class KeyClassEnum(IntEnum): # pragma: no cover
@public
class KeyAgreementEnum(IntEnum): # pragma: no cover
"""JavaCard `KeyAgreement` type values."""
+
ALG_EC_SVDP_DH = 1
ALG_EC_SVDP_DH_KDF = 1
ALG_EC_SVDP_DHC = 2
@@ -140,6 +149,7 @@ class KeyAgreementEnum(IntEnum): # pragma: no cover
@public
class SignatureEnum(IntEnum): # pragma: no cover
"""JavaCard `Signature` type values."""
+
ALG_ECDSA_SHA = 17
ALG_ECDSA_SHA_224 = 37
ALG_ECDSA_SHA_256 = 33
@@ -150,6 +160,7 @@ class SignatureEnum(IntEnum): # pragma: no cover
@public
class TransformationEnum(ShiftableFlag): # pragma: no cover
"""ECTester's point/value transformation types."""
+
NONE = 0x00
FIXED = 0x01
FULLRANDOM = 0x02
@@ -167,6 +178,7 @@ class TransformationEnum(ShiftableFlag): # pragma: no cover
@public
class FormatEnum(IntEnum): # pragma: no cover
"""ECTester's point format types."""
+
UNCOMPRESSED = 0
COMPRESSED = 1
HYBRID = 2
@@ -175,8 +187,9 @@ class FormatEnum(IntEnum): # pragma: no cover
@public
class CurveEnum(IntEnum): # pragma: no cover
"""ECTester's curve constants."""
+
default = 0x00
- external = 0xff
+ external = 0xFF
secp112r1 = 0x01
secp128r1 = 0x02
secp160r1 = 0x03
@@ -186,15 +199,16 @@ class CurveEnum(IntEnum): # pragma: no cover
secp384r1 = 0x07
secp521r1 = 0x08
sect163r1 = 0x09
- sect233r1 = 0x0a
- sect283r1 = 0x0b
- sect409r1 = 0x0c
- sect571r1 = 0x0d
+ sect233r1 = 0x0A
+ sect283r1 = 0x0B
+ sect409r1 = 0x0C
+ sect571r1 = 0x0D
@public
class ParameterEnum(ShiftableFlag): # pragma: no cover
"""ECTester's parameter ids."""
+
NONE = 0x00
FP = 0x01
F2M = 0x02
@@ -214,11 +228,13 @@ class ParameterEnum(ShiftableFlag): # pragma: no cover
@public
class ChunkingException(Exception): # pragma: no cover
"""An exception that is raised if an error happened during the chunking process of a large APDU."""
+
pass
class Response(ABC): # pragma: no cover
"""An abstract base class of a response APDU."""
+
resp: ResponseAPDU
sws: List[int]
params: List[bytes]
@@ -233,7 +249,7 @@ class Response(ABC): # pragma: no cover
offset = 0
for i in range(num_sw):
if len(resp.data) >= offset + 2:
- self.sws[i] = int.from_bytes(resp.data[offset:offset + 2], "big")
+ self.sws[i] = int.from_bytes(resp.data[offset : offset + 2], "big")
offset += 2
if self.sws[i] != ISO7816.SW_NO_ERROR:
self.success = False
@@ -250,13 +266,13 @@ class Response(ABC): # pragma: no cover
self.success = False
self.error = True
break
- param_len = int.from_bytes(resp.data[offset:offset + 2], "big")
+ param_len = int.from_bytes(resp.data[offset : offset + 2], "big")
offset += 2
if len(resp.data) < offset + param_len:
self.success = False
self.error = True
break
- self.params[i] = resp.data[offset:offset + param_len]
+ self.params[i] = resp.data[offset : offset + param_len]
offset += param_len
def __repr__(self):
@@ -322,12 +338,18 @@ class GenerateResponse(Response): # pragma: no cover
@public
class ExportResponse(Response): # pragma: no cover
"""A response to the Export command, contains the exported parameters/values."""
+
keypair: KeypairEnum
key: KeyEnum
parameters: ParameterEnum
- def __init__(self, resp: ResponseAPDU, keypair: KeypairEnum, key: KeyEnum,
- params: ParameterEnum):
+ def __init__(
+ self,
+ resp: ResponseAPDU,
+ keypair: KeypairEnum,
+ key: KeyEnum,
+ params: ParameterEnum,
+ ):
self.keypair = keypair
self.key = key
self.parameters = params
@@ -344,7 +366,9 @@ class ExportResponse(Response): # pragma: no cover
other = 0
other += 1 if key & KeyEnum.PUBLIC and params & ParameterEnum.W else 0
other += 1 if key & KeyEnum.PRIVATE and params & ParameterEnum.S else 0
- super().__init__(resp, exported, exported * keys * param_count + exported * other)
+ super().__init__(
+ resp, exported, exported * keys * param_count + exported * other
+ )
def get_index(self, keypair: KeypairEnum, param: ParameterEnum) -> Optional[int]:
pair = KeypairEnum.KEYPAIR_LOCAL
@@ -378,8 +402,10 @@ class ExportResponse(Response): # pragma: no cover
return None
def __repr__(self):
- return f"{self.__class__.__name__}(sws=[{', '.join(list(map(hex, self.sws)))}], sw={hex(self.resp.sw)}, success={self.success}, error={self.error}, " \
- f"keypair={self.keypair.name}, key={self.key.name}, params={self.parameters.name})"
+ return (
+ f"{self.__class__.__name__}(sws=[{', '.join(list(map(hex, self.sws)))}], sw={hex(self.resp.sw)}, success={self.success}, error={self.error}, "
+ f"keypair={self.keypair.name}, key={self.key.name}, params={self.parameters.name})"
+ )
@public
@@ -434,6 +460,7 @@ class RunModeResponse(Response): # pragma: no cover
class InfoResponse(Response): # pragma: no cover
"""A response to the Info command, contains all information about the applet version/environment."""
+
version: str
base: AppletBaseEnum
system_version: float
@@ -447,33 +474,39 @@ class InfoResponse(Response): # pragma: no cover
super().__init__(resp, 1, 0)
offset = 2
- version_len = int.from_bytes(resp.data[offset:offset + 2], "big")
+ version_len = int.from_bytes(resp.data[offset : offset + 2], "big")
offset += 2
- self.version = resp.data[offset:offset + version_len].decode()
+ self.version = resp.data[offset : offset + version_len].decode()
offset += version_len
- self.base = AppletBaseEnum(int.from_bytes(resp.data[offset:offset + 2], "big"))
+ self.base = AppletBaseEnum(
+ int.from_bytes(resp.data[offset : offset + 2], "big")
+ )
offset += 2
- system_version = int.from_bytes(resp.data[offset:offset + 2], "big")
+ system_version = int.from_bytes(resp.data[offset : offset + 2], "big")
system_major = system_version >> 8
- system_minor = system_version & 0xff
+ system_minor = system_version & 0xFF
minor_size = 1 if system_minor == 0 else ceil(log(system_minor, 10))
self.system_version = system_major + system_minor / (minor_size * 10)
offset += 2
- self.object_deletion_supported = int.from_bytes(resp.data[offset:offset + 2], "big") == 1
+ self.object_deletion_supported = (
+ int.from_bytes(resp.data[offset : offset + 2], "big") == 1
+ )
offset += 2
- self.buf_len = int.from_bytes(resp.data[offset:offset + 2], "big")
+ self.buf_len = int.from_bytes(resp.data[offset : offset + 2], "big")
offset += 2
- self.ram1_len = int.from_bytes(resp.data[offset:offset + 2], "big")
+ self.ram1_len = int.from_bytes(resp.data[offset : offset + 2], "big")
offset += 2
- self.ram2_len = int.from_bytes(resp.data[offset:offset + 2], "big")
+ self.ram2_len = int.from_bytes(resp.data[offset : offset + 2], "big")
offset += 2
- self.apdu_len = int.from_bytes(resp.data[offset:offset + 2], "big")
+ self.apdu_len = int.from_bytes(resp.data[offset : offset + 2], "big")
offset += 2
def __repr__(self):
- return f"{self.__class__.__name__}(sws=[{', '.join(list(map(hex, self.sws)))}], sw={hex(self.resp.sw)}, " \
- f"success={self.success}, error={self.error}, version={self.version}, base={self.base.name}, system_version={self.system_version}, " \
- f"object_deletion_supported={self.object_deletion_supported}, buf_len={self.buf_len}, ram1_len={self.ram1_len}, ram2_len={self.ram2_len}, apdu_len={self.apdu_len})"
+ return (
+ f"{self.__class__.__name__}(sws=[{', '.join(list(map(hex, self.sws)))}], sw={hex(self.resp.sw)}, "
+ f"success={self.success}, error={self.error}, version={self.version}, base={self.base.name}, system_version={self.system_version}, "
+ f"object_deletion_supported={self.object_deletion_supported}, buf_len={self.buf_len}, ram1_len={self.ram1_len}, ram2_len={self.ram2_len}, apdu_len={self.apdu_len})"
+ )
@public
@@ -482,7 +515,8 @@ 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.
"""
- CLA_ECTESTER = 0xb0
+
+ CLA_ECTESTER = 0xB0
AID_PREFIX = bytes([0x45, 0x43, 0x54, 0x65, 0x73, 0x74, 0x65, 0x72])
AID_CURRENT_VERSION = bytes([0x30, 0x33, 0x33]) # Version v0.3.3
AID_SUFFIX_221 = bytes([0x62])
@@ -507,15 +541,19 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
chunk_length = 255
if chunk_start + chunk_length > len(data):
chunk_length = len(data) - chunk_start
- chunk = data[chunk_start: chunk_start + chunk_length]
- chunk_apdu = CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_BUFFER, 0, 0, chunk)
+ chunk = data[chunk_start : chunk_start + chunk_length]
+ chunk_apdu = CommandAPDU(
+ self.CLA_ECTESTER, InstructionEnum.INS_BUFFER, 0, 0, chunk
+ )
resp = super().send_apdu(chunk_apdu)
if resp.sw != 0x9000:
raise ChunkingException()
apdu = CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_PERFORM, 0, 0)
resp = super().send_apdu(apdu)
- if resp.sw & 0xff00 == ISO7816.SW_BYTES_REMAINING_00:
- resp = super().send_apdu(CommandAPDU(0x00, 0xc0, 0x00, 0x00, None, resp.sw & 0xff))
+ if resp.sw & 0xFF00 == ISO7816.SW_BYTES_REMAINING_00:
+ resp = super().send_apdu(
+ CommandAPDU(0x00, 0xC0, 0x00, 0x00, None, resp.sw & 0xFF)
+ )
return resp
def select_applet(self, latest_version: bytes = AID_CURRENT_VERSION):
@@ -548,7 +586,9 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
return True
@staticmethod
- def encode_parameters(params: ParameterEnum, obj: Union[DomainParameters, Point, int]) -> Mapping[ParameterEnum, bytes]:
+ def encode_parameters(
+ params: ParameterEnum, obj: Union[DomainParameters, Point, int]
+ ) -> Mapping[ParameterEnum, bytes]:
"""Encode values from `obj` into the byte parameters that the **ECTester** applet expects."""
def convert_int(obj: int) -> bytes:
@@ -559,7 +599,9 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
return bytes(obj)
result = {}
- if isinstance(obj, DomainParameters) and isinstance(obj.curve.model, ShortWeierstrassModel):
+ if isinstance(obj, DomainParameters) and isinstance(
+ obj.curve.model, ShortWeierstrassModel
+ ):
for param in params & ParameterEnum.DOMAIN_FP:
if param == ParameterEnum.G:
result[param] = convert_point(obj.generator.to_affine())
@@ -577,7 +619,9 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
for param in params & (ParameterEnum.G | ParameterEnum.W):
result[param] = convert_point(obj)
elif isinstance(obj, int):
- for param in params & ((ParameterEnum.DOMAIN_FP ^ ParameterEnum.G) | ParameterEnum.S):
+ for param in params & (
+ (ParameterEnum.DOMAIN_FP ^ ParameterEnum.G) | ParameterEnum.S
+ ):
result[param] = convert_int(obj)
else:
raise TypeError
@@ -591,8 +635,14 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
:return: The response.
"""
resp = self.send_apdu(
- CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_ALLOCATE_KA, 0, 0,
- bytes([ka_type])))
+ CommandAPDU(
+ self.CLA_ECTESTER,
+ InstructionEnum.INS_ALLOCATE_KA,
+ 0,
+ 0,
+ bytes([ka_type]),
+ )
+ )
return AllocateKaResponse(resp)
def allocate_sig(self, sig_type: SignatureEnum) -> AllocateSigResponse:
@@ -602,12 +652,24 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
:param sig_type: Which Signature type to allocate.
:return: The response.
"""
- resp = self.send_apdu(CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_ALLOCATE_SIG, 0, 0,
- bytes([sig_type])))
+ resp = self.send_apdu(
+ CommandAPDU(
+ self.CLA_ECTESTER,
+ InstructionEnum.INS_ALLOCATE_SIG,
+ 0,
+ 0,
+ bytes([sig_type]),
+ )
+ )
return AllocateSigResponse(resp)
- def allocate(self, keypair: KeypairEnum, builder: KeyBuildEnum, key_length: int,
- key_class: KeyClassEnum) -> AllocateResponse:
+ def allocate(
+ self,
+ keypair: KeypairEnum,
+ builder: KeyBuildEnum,
+ key_length: int,
+ key_class: KeyClassEnum,
+ ) -> AllocateResponse:
"""
Send the Allocate KeyPair command.
@@ -618,8 +680,14 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
:return: The response.
"""
resp = self.send_apdu(
- CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_ALLOCATE, keypair, builder,
- key_length.to_bytes(2, "big") + bytes([key_class])))
+ CommandAPDU(
+ self.CLA_ECTESTER,
+ InstructionEnum.INS_ALLOCATE,
+ keypair,
+ builder,
+ key_length.to_bytes(2, "big") + bytes([key_class]),
+ )
+ )
return AllocateResponse(resp, keypair)
def clear(self, keypair: KeypairEnum) -> ClearResponse:
@@ -630,11 +698,17 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
:return: The response.
"""
resp = self.send_apdu(
- CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_CLEAR, keypair, 0, None))
+ CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_CLEAR, keypair, 0, None)
+ )
return ClearResponse(resp, keypair)
- def set(self, keypair: KeypairEnum, curve: CurveEnum, params: ParameterEnum,
- values: Optional[Mapping[ParameterEnum, bytes]] = None) -> SetResponse:
+ def set(
+ self,
+ keypair: KeypairEnum,
+ curve: CurveEnum,
+ params: ParameterEnum,
+ values: Optional[Mapping[ParameterEnum, bytes]] = None,
+ ) -> SetResponse:
"""
Send the Set command.
@@ -656,18 +730,31 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
break
e <<= 1
resp = self.send_apdu(
- CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_SET, keypair, curve,
- payload))
+ CommandAPDU(
+ self.CLA_ECTESTER, InstructionEnum.INS_SET, keypair, curve, payload
+ )
+ )
elif values is not None:
raise ValueError("Values should be specified only if curve is external.")
else:
resp = self.send_apdu(
- CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_SET, keypair, curve,
- params.to_bytes(2, "big")))
+ CommandAPDU(
+ self.CLA_ECTESTER,
+ InstructionEnum.INS_SET,
+ keypair,
+ curve,
+ params.to_bytes(2, "big"),
+ )
+ )
return SetResponse(resp, keypair)
- def transform(self, keypair: KeypairEnum, key: KeyEnum, params: ParameterEnum,
- transformation: TransformationEnum) -> TransformResponse:
+ def transform(
+ self,
+ keypair: KeypairEnum,
+ key: KeyEnum,
+ params: ParameterEnum,
+ transformation: TransformationEnum,
+ ) -> TransformResponse:
"""
Send the Transform command.
@@ -678,8 +765,14 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
:return: The response.
"""
resp = self.send_apdu(
- CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_TRANSFORM, keypair, key,
- params.to_bytes(2, "big") + transformation.to_bytes(2, "big")))
+ CommandAPDU(
+ self.CLA_ECTESTER,
+ InstructionEnum.INS_TRANSFORM,
+ keypair,
+ key,
+ params.to_bytes(2, "big") + transformation.to_bytes(2, "big"),
+ )
+ )
return TransformResponse(resp, keypair)
def generate(self, keypair: KeypairEnum) -> GenerateResponse:
@@ -690,10 +783,15 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
:return: The response.
"""
resp = self.send_apdu(
- CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_GENERATE, keypair, 0, None))
+ CommandAPDU(
+ self.CLA_ECTESTER, InstructionEnum.INS_GENERATE, keypair, 0, None
+ )
+ )
return GenerateResponse(resp, keypair)
- def export(self, keypair: KeypairEnum, key: KeyEnum, params: ParameterEnum) -> ExportResponse:
+ def export(
+ self, keypair: KeypairEnum, key: KeyEnum, params: ParameterEnum
+ ) -> ExportResponse:
"""
Send the Export command.
@@ -703,12 +801,24 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
:return: The response, containing the exported parameters.
"""
resp = self.send_apdu(
- CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_EXPORT, keypair, key,
- params.to_bytes(2, "big")))
+ CommandAPDU(
+ self.CLA_ECTESTER,
+ InstructionEnum.INS_EXPORT,
+ keypair,
+ key,
+ params.to_bytes(2, "big"),
+ )
+ )
return ExportResponse(resp, keypair, key, params)
- def ecdh(self, pubkey: KeypairEnum, privkey: KeypairEnum, export: bool,
- transformation: TransformationEnum, ka_type: KeyAgreementEnum) -> ECDHResponse:
+ def ecdh(
+ self,
+ pubkey: KeypairEnum,
+ privkey: KeypairEnum,
+ export: bool,
+ transformation: TransformationEnum,
+ ka_type: KeyAgreementEnum,
+ ) -> ECDHResponse:
"""
Send the ECDH command.
@@ -720,13 +830,26 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
:return: The response.
"""
resp = self.send_apdu(
- CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_ECDH, pubkey, privkey,
- bytes([ExportEnum.from_bool(export)]) + transformation.to_bytes(
- 2, "big") + bytes([ka_type])))
+ CommandAPDU(
+ self.CLA_ECTESTER,
+ InstructionEnum.INS_ECDH,
+ pubkey,
+ privkey,
+ bytes([ExportEnum.from_bool(export)])
+ + transformation.to_bytes(2, "big")
+ + bytes([ka_type]),
+ )
+ )
return ECDHResponse(resp, export)
- def ecdh_direct(self, privkey: KeypairEnum, export: bool, transformation: TransformationEnum,
- ka_type: KeyAgreementEnum, pubkey: bytes) -> ECDHResponse:
+ def ecdh_direct(
+ self,
+ privkey: KeypairEnum,
+ export: bool,
+ transformation: TransformationEnum,
+ ka_type: KeyAgreementEnum,
+ pubkey: bytes,
+ ) -> ECDHResponse:
"""
Send the ECDH direct command.
@@ -738,14 +861,22 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
:return: The response.
"""
resp = self.send_apdu(
- CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_ECDH_DIRECT, privkey,
- ExportEnum.from_bool(export),
- transformation.to_bytes(2, "big") + bytes([ka_type]) + len(
- pubkey).to_bytes(2, "big") + pubkey))
+ CommandAPDU(
+ self.CLA_ECTESTER,
+ InstructionEnum.INS_ECDH_DIRECT,
+ privkey,
+ ExportEnum.from_bool(export),
+ transformation.to_bytes(2, "big")
+ + bytes([ka_type])
+ + len(pubkey).to_bytes(2, "big")
+ + pubkey,
+ )
+ )
return ECDHResponse(resp, export)
- def ecdsa(self, keypair: KeypairEnum, export: bool, sig_type: SignatureEnum,
- data: bytes) -> ECDSAResponse:
+ def ecdsa(
+ self, keypair: KeypairEnum, export: bool, sig_type: SignatureEnum, data: bytes
+ ) -> ECDSAResponse:
"""
Send the ECDSA command.
@@ -755,13 +886,20 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
:param data: The data to sign and verify.
:return: The response.
"""
- resp = self.send_apdu(CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_ECDSA, keypair,
- ExportEnum.from_bool(export),
- bytes([sig_type]) + len(data).to_bytes(2, "big") + data))
+ resp = self.send_apdu(
+ CommandAPDU(
+ self.CLA_ECTESTER,
+ InstructionEnum.INS_ECDSA,
+ keypair,
+ ExportEnum.from_bool(export),
+ bytes([sig_type]) + len(data).to_bytes(2, "big") + data,
+ )
+ )
return ECDSAResponse(resp, export)
- def ecdsa_sign(self, keypair: KeypairEnum, export: bool, sig_type: SignatureEnum,
- data: bytes) -> ECDSAResponse:
+ def ecdsa_sign(
+ self, keypair: KeypairEnum, export: bool, sig_type: SignatureEnum, data: bytes
+ ) -> ECDSAResponse:
"""
Send the ECDSA sign command.
@@ -772,13 +910,19 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
:return: The response.
"""
resp = self.send_apdu(
- CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_ECDSA_SIGN, keypair,
- ExportEnum.from_bool(export),
- bytes([sig_type]) + len(data).to_bytes(2, "big") + data))
+ CommandAPDU(
+ self.CLA_ECTESTER,
+ InstructionEnum.INS_ECDSA_SIGN,
+ keypair,
+ ExportEnum.from_bool(export),
+ bytes([sig_type]) + len(data).to_bytes(2, "big") + data,
+ )
+ )
return ECDSAResponse(resp, export)
- def ecdsa_verify(self, keypair: KeypairEnum, sig_type: SignatureEnum, sig: bytes,
- data: bytes) -> ECDSAResponse:
+ def ecdsa_verify(
+ self, keypair: KeypairEnum, sig_type: SignatureEnum, sig: bytes, data: bytes
+ ) -> ECDSAResponse:
"""
Send the ECDSA verify command.
@@ -788,10 +932,15 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
:param data: The data.
:return: The response.
"""
- resp = self.send_apdu(CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_ECDSA_VERIFY,
- keypair, sig_type,
- len(data).to_bytes(2, "big") + data + len(sig).to_bytes(2,
- "big") + sig))
+ resp = self.send_apdu(
+ CommandAPDU(
+ self.CLA_ECTESTER,
+ InstructionEnum.INS_ECDSA_VERIFY,
+ keypair,
+ sig_type,
+ len(data).to_bytes(2, "big") + data + len(sig).to_bytes(2, "big") + sig,
+ )
+ )
return ECDSAResponse(resp, False)
def cleanup(self) -> CleanupResponse:
@@ -801,7 +950,8 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
:return: The response.
"""
resp = self.send_apdu(
- CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_CLEANUP, 0, 0, None))
+ CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_CLEANUP, 0, 0, None)
+ )
return CleanupResponse(resp)
def info(self) -> InfoResponse:
@@ -811,7 +961,8 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
:return: The response.
"""
resp = self.send_apdu(
- CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_GET_INFO, 0, 0, None))
+ CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_GET_INFO, 0, 0, None)
+ )
return InfoResponse(resp)
def run_mode(self, run_mode: RunModeEnum) -> RunModeResponse:
@@ -821,6 +972,12 @@ class ECTesterTarget(PCSCTarget): # pragma: no cover
:return: The response.
"""
resp = self.send_apdu(
- CommandAPDU(self.CLA_ECTESTER, InstructionEnum.INS_SET_DRY_RUN_MODE, run_mode, 0,
- None))
+ CommandAPDU(
+ self.CLA_ECTESTER,
+ InstructionEnum.INS_SET_DRY_RUN_MODE,
+ run_mode,
+ 0,
+ None,
+ )
+ )
return RunModeResponse(resp)
diff --git a/pyecsca/sca/target/simpleserial.py b/pyecsca/sca/target/simpleserial.py
index 03e5768..03cb9cf 100644
--- a/pyecsca/sca/target/simpleserial.py
+++ b/pyecsca/sca/target/simpleserial.py
@@ -13,6 +13,7 @@ from .serial import SerialTarget
@public
class SimpleSerialMessage(object):
"""A SimpleSerial message consisting of a starting character and a hexadecimal string."""
+
char: str
data: str
@@ -60,7 +61,9 @@ class SimpleSerialTarget(SerialTarget):
result[msg.char] = msg
return result
- def send_cmd(self, cmd: SimpleSerialMessage, timeout: int) -> Mapping[str, SimpleSerialMessage]:
+ def send_cmd(
+ 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`.
@@ -71,7 +74,7 @@ class SimpleSerialTarget(SerialTarget):
"""
data = bytes(cmd)
for i in range(0, len(data), 64):
- chunk = data[i:i + 64]
+ chunk = data[i : i + 64]
sleep(0.010)
self.write(chunk)
self.write(b"\n")
diff --git a/pyecsca/sca/trace/align.py b/pyecsca/sca/trace/align.py
index 56be5b3..d97e358 100644
--- a/pyecsca/sca/trace/align.py
+++ b/pyecsca/sca/trace/align.py
@@ -11,8 +11,9 @@ from .process import normalize
from .trace import Trace
-def _align_reference(reference: Trace, *traces: Trace,
- align_func: Callable[[Trace], Tuple[bool, int]]) -> Tuple[List[Trace], List[int]]:
+def _align_reference(
+ reference: Trace, *traces: Trace, align_func: Callable[[Trace], Tuple[bool, int]]
+) -> Tuple[List[Trace], List[int]]:
result = [deepcopy(reference)]
offsets = [0]
for trace in traces:
@@ -25,18 +26,23 @@ def _align_reference(reference: Trace, *traces: Trace,
else:
result_samples = np.zeros(len(trace.samples), dtype=trace.samples.dtype)
if offset > 0:
- result_samples[:length - offset] = trace.samples[offset:]
+ result_samples[: length - offset] = trace.samples[offset:]
else:
- result_samples[-offset:] = trace.samples[:length + offset]
+ result_samples[-offset:] = trace.samples[: length + offset]
result.append(trace.with_samples(result_samples))
offsets.append(offset)
return result, offsets
@public
-def align_correlation(reference: Trace, *traces: Trace,
- reference_offset: int, reference_length: int,
- max_offset: int, min_correlation: float = 0.5) -> Tuple[List[Trace], List[int]]:
+def align_correlation(
+ reference: Trace,
+ *traces: Trace,
+ reference_offset: int,
+ reference_length: int,
+ max_offset: int,
+ 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
trace starting at `reference_offset` with `reference_length` and try to match it to a part of
@@ -54,14 +60,19 @@ def align_correlation(reference: Trace, *traces: Trace,
"""
reference_centered = normalize(reference)
reference_part = reference_centered.samples[
- reference_offset:reference_offset + reference_length]
+ reference_offset : reference_offset + reference_length
+ ]
def align_func(trace):
length = len(trace.samples)
correlation_start = max(reference_offset - max_offset, 0)
- correlation_end = min(reference_offset + reference_length + max_offset, length - 1)
+ correlation_end = min(
+ reference_offset + reference_length + max_offset, length - 1
+ )
trace_part = trace.samples[correlation_start:correlation_end]
- trace_part = (trace_part - np.mean(trace_part)) / (np.std(trace_part) * len(trace_part))
+ trace_part = (trace_part - np.mean(trace_part)) / (
+ np.std(trace_part) * len(trace_part)
+ )
correlation = np.correlate(trace_part, reference_part, "same")
max_correlation_offset = correlation.argmax(axis=0)
max_correlation = correlation[max_correlation_offset]
@@ -76,8 +87,13 @@ def align_correlation(reference: Trace, *traces: Trace,
@public
-def align_peaks(reference: Trace, *traces: Trace,
- reference_offset: int, reference_length: int, max_offset: int) -> Tuple[List[Trace], List[int]]:
+def align_peaks(
+ reference: Trace,
+ *traces: Trace,
+ reference_offset: int,
+ reference_length: int,
+ max_offset: int
+) -> Tuple[List[Trace], List[int]]:
"""
Align `traces` to the reference `trace` so that the maximum value within the reference trace
window from `reference_offset` of `reference_length` aligns with the maximum
@@ -90,14 +106,16 @@ def align_peaks(reference: Trace, *traces: Trace,
:param max_offset:
:return:
"""
- reference_part = reference.samples[reference_offset: reference_offset + reference_length]
+ reference_part = reference.samples[
+ reference_offset : reference_offset + reference_length
+ ]
reference_peak = np.argmax(reference_part)
def align_func(trace):
length = len(trace.samples)
window_start = max(reference_offset - max_offset, 0)
window_end = min(reference_offset + reference_length + max_offset, length - 1)
- window = trace.samples[window_start: window_end]
+ window = trace.samples[window_start:window_end]
window_peak = np.argmax(window)
left_space = min(max_offset, reference_offset)
return True, int(window_peak - reference_peak - left_space)
@@ -106,9 +124,15 @@ def align_peaks(reference: Trace, *traces: Trace,
@public
-def align_offset(reference: Trace, *traces: Trace,
- reference_offset: int, reference_length: int, max_offset: int,
- dist_func: Callable[[np.ndarray, np.ndarray], float], max_dist: float = float("inf")) -> Tuple[List[Trace], List[int]]:
+def align_offset(
+ reference: Trace,
+ *traces: Trace,
+ reference_offset: int,
+ reference_length: int,
+ max_offset: int,
+ dist_func: Callable[[np.ndarray, np.ndarray], float],
+ 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
between the reference trace window from `reference_offset` of `reference_length` and the trace
@@ -122,7 +146,9 @@ def align_offset(reference: Trace, *traces: Trace,
:param dist_func:
:return:
"""
- reference_part = reference.samples[reference_offset: reference_offset + reference_length]
+ reference_part = reference.samples[
+ reference_offset : reference_offset + reference_length
+ ]
def align_func(trace):
length = len(trace.samples)
@@ -142,12 +168,18 @@ def align_offset(reference: Trace, *traces: Trace,
return True, best_offset
else:
return False, 0
+
return _align_reference(reference, *traces, align_func=align_func)
@public
-def align_sad(reference: Trace, *traces: Trace,
- reference_offset: int, reference_length: int, max_offset: int) -> Tuple[List[Trace], List[int]]:
+def align_sad(
+ reference: Trace,
+ *traces: Trace,
+ reference_offset: int,
+ reference_length: int,
+ max_offset: int
+) -> Tuple[List[Trace], List[int]]:
"""
Align `traces` to the reference `trace` so that the Sum Of Absolute Differences between the
reference trace window from `reference_offset` of `reference_length` and the trace being aligned
@@ -160,17 +192,24 @@ def align_sad(reference: Trace, *traces: Trace,
:param max_offset:
:return:
"""
+
def sad(reference_part, trace_part):
return float(np.sum(np.abs(reference_part - trace_part)))
- return align_offset(reference, *traces,
- reference_offset=reference_offset, reference_length=reference_length,
- max_offset=max_offset, dist_func=sad)
+ return align_offset(
+ reference,
+ *traces,
+ reference_offset=reference_offset,
+ reference_length=reference_length,
+ max_offset=max_offset,
+ dist_func=sad
+ )
@public
-def align_dtw_scale(reference: Trace, *traces: Trace, radius: int = 1,
- fast: bool = True) -> List[Trace]:
+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:
@@ -206,7 +245,9 @@ def align_dtw_scale(reference: Trace, *traces: Trace, radius: int = 1,
@public
-def align_dtw(reference: Trace, *traces: Trace, radius: int = 1, fast: bool = True) -> List[Trace]:
+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:
@@ -228,9 +269,13 @@ def align_dtw(reference: Trace, *traces: Trace, radius: int = 1, fast: bool = Tr
dist, path = fastdtw(reference_samples, trace.samples, radius=radius)
else:
dist, path = dtw(reference_samples, trace.samples)
- result_samples = np.zeros(max((len(trace.samples), len(reference_samples))), dtype=trace.samples.dtype)
- pairs = np.array(np.array(path, dtype=np.dtype("int,int")),
- dtype=np.dtype([("x", "int"), ("y", "int")]))
+ result_samples = np.zeros(
+ max((len(trace.samples), len(reference_samples))), dtype=trace.samples.dtype
+ )
+ pairs = np.array(
+ np.array(path, dtype=np.dtype("int,int")),
+ dtype=np.dtype([("x", "int"), ("y", "int")]),
+ )
result_samples[pairs["x"]] = trace.samples[pairs["y"]]
del pairs
del path
diff --git a/pyecsca/sca/trace/combine.py b/pyecsca/sca/trace/combine.py
index 8b80869..853b127 100644
--- a/pyecsca/sca/trace/combine.py
+++ b/pyecsca/sca/trace/combine.py
@@ -25,13 +25,15 @@ def average(*traces: Trace) -> Optional[CombinedTrace]:
s = np.zeros(min_samples, dtype=np.float64)
for t in traces:
s = np.add(s, t.samples[:min_samples])
- avg = ((1 / len(traces)) * s)
+ avg = (1 / len(traces)) * s
del s
return CombinedTrace(avg)
@public
-def conditional_average(*traces: Trace, condition: Callable[[Trace], bool]) -> Optional[CombinedTrace]:
+def conditional_average(
+ *traces: Trace, condition: Callable[[Trace], bool]
+) -> Optional[CombinedTrace]:
"""
Average `traces` for which the `condition` is True, sample-wise.
@@ -107,8 +109,10 @@ def average_and_variance(*traces) -> Optional[Tuple[CombinedTrace, CombinedTrace
if not traces:
return None
if len(traces) == 1:
- return (CombinedTrace(traces[0].samples.copy()),
- CombinedTrace(np.zeros(len(traces[0]), dtype=np.float64)))
+ return (
+ CombinedTrace(traces[0].samples.copy()),
+ CombinedTrace(np.zeros(len(traces[0]), dtype=np.float64)),
+ )
min_samples = min(map(len, traces))
s = np.zeros(min_samples, dtype=np.float64)
for t in traces:
diff --git a/pyecsca/sca/trace/edit.py b/pyecsca/sca/trace/edit.py
index c0af078..5aec15f 100644
--- a/pyecsca/sca/trace/edit.py
+++ b/pyecsca/sca/trace/edit.py
@@ -39,8 +39,11 @@ def reverse(trace: Trace) -> Trace:
@public
-def pad(trace: Trace, lengths: Union[Tuple[int, int], int],
- values: Union[Tuple[Any, Any], Any] = (0, 0)) -> Trace:
+def pad(
+ trace: Trace,
+ lengths: Union[Tuple[int, int], int],
+ values: Union[Tuple[Any, Any], Any] = (0, 0),
+) -> Trace:
"""
Pad the samples of the `trace` by `values` at the beginning and end.
@@ -53,4 +56,6 @@ def pad(trace: Trace, lengths: Union[Tuple[int, int], int],
lengths = (lengths, lengths)
if not isinstance(values, tuple):
values = (values, values)
- return trace.with_samples(np.pad(trace.samples, lengths, "constant", constant_values=values))
+ return trace.with_samples(
+ np.pad(trace.samples, lengths, "constant", constant_values=values)
+ )
diff --git a/pyecsca/sca/trace/filter.py b/pyecsca/sca/trace/filter.py
index 720d311..2fbc284 100644
--- a/pyecsca/sca/trace/filter.py
+++ b/pyecsca/sca/trace/filter.py
@@ -8,13 +8,23 @@ from typing import Union, Tuple
from .trace import Trace
-def _filter_any(trace: Trace, sampling_frequency: int,
- cutoff: Union[int, Tuple[int, int]], band_type: str) -> Trace:
+def _filter_any(
+ trace: Trace,
+ sampling_frequency: int,
+ cutoff: Union[int, Tuple[int, int]],
+ band_type: str,
+) -> Trace:
nyq = 0.5 * sampling_frequency
if not isinstance(cutoff, int):
- b, a = butter(6, tuple(map(lambda x: x / nyq, cutoff)), btype=band_type, analog=False, output='ba')
+ b, a = butter(
+ 6,
+ tuple(map(lambda x: x / nyq, cutoff)),
+ btype=band_type,
+ analog=False,
+ output="ba",
+ )
else:
- b, a = butter(6, cutoff / nyq, btype=band_type, analog=False, output='ba')
+ b, a = butter(6, cutoff / nyq, btype=band_type, analog=False, output="ba")
return trace.with_samples(lfilter(b, a, trace.samples))
@@ -47,7 +57,9 @@ def filter_highpass(trace: Trace, sampling_frequency: int, cutoff: int) -> Trace
@public
-def filter_bandpass(trace: Trace, sampling_frequency: int, low: int, high: int) -> Trace:
+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`.
@@ -62,7 +74,9 @@ def filter_bandpass(trace: Trace, sampling_frequency: int, low: int, high: int)
@public
-def filter_bandstop(trace: Trace, sampling_frequency: int, low: int, high: int) -> Trace:
+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`.
diff --git a/pyecsca/sca/trace/match.py b/pyecsca/sca/trace/match.py
index 3728d13..50c86bc 100644
--- a/pyecsca/sca/trace/match.py
+++ b/pyecsca/sca/trace/match.py
@@ -43,7 +43,9 @@ def match_pattern(trace: Trace, pattern: Trace, threshold: float = 0.8) -> List[
@public
-def match_part(trace: Trace, offset: int, length: int, threshold: float = 0.8) -> List[int]:
+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
diff --git a/pyecsca/sca/trace/plot.py b/pyecsca/sca/trace/plot.py
index b9843cc..6a8cec1 100644
--- a/pyecsca/sca/trace/plot.py
+++ b/pyecsca/sca/trace/plot.py
@@ -40,10 +40,12 @@ def plot_traces(*traces: Trace, **kwargs): # pragma: no cover
["orange", "darkorange"],
["plum", "deeppink"],
["peru", "chocolate"],
- ["cyan", "darkcyan"]
+ ["cyan", "darkcyan"],
]
dss = []
for i, trace in enumerate(traces):
- line = hv.Curve((range(len(trace)), trace.samples), kdims="x", vdims="y", **kwargs)
+ line = hv.Curve(
+ (range(len(trace)), trace.samples), kdims="x", vdims="y", **kwargs
+ )
dss.append(datashade(line, normalization="log", cmap=_cmaps[i % len(_cmaps)]))
return reduce(lambda x, y: x * y, dss)
diff --git a/pyecsca/sca/trace/process.py b/pyecsca/sca/trace/process.py
index 87b00ee..2499df4 100644
--- a/pyecsca/sca/trace/process.py
+++ b/pyecsca/sca/trace/process.py
@@ -61,8 +61,14 @@ def rolling_mean(trace: Trace, window: int) -> Trace:
:param window:
:return:
"""
- return trace.with_samples(cast(np.ndarray, np.mean(_rolling_window(trace.samples, window), -1).astype(
- dtype=trace.samples.dtype, copy=False)))
+ return trace.with_samples(
+ cast(
+ np.ndarray,
+ np.mean(_rolling_window(trace.samples, window), -1).astype(
+ dtype=trace.samples.dtype, copy=False
+ ),
+ )
+ )
@public
@@ -101,7 +107,9 @@ def normalize(trace: Trace) -> Trace:
:param trace:
:return:
"""
- return trace.with_samples((trace.samples - np.mean(trace.samples)) / np.std(trace.samples))
+ return trace.with_samples(
+ (trace.samples - np.mean(trace.samples)) / np.std(trace.samples)
+ )
@public
@@ -112,5 +120,7 @@ def normalize_wl(trace: Trace) -> Trace:
:param trace:
:return:
"""
- return trace.with_samples((trace.samples - np.mean(trace.samples)) / (
- np.std(trace.samples) * len(trace.samples)))
+ return trace.with_samples(
+ (trace.samples - np.mean(trace.samples))
+ / (np.std(trace.samples) * len(trace.samples))
+ )
diff --git a/pyecsca/sca/trace/sampling.py b/pyecsca/sca/trace/sampling.py
index b263adc..71dbdac 100644
--- a/pyecsca/sca/trace/sampling.py
+++ b/pyecsca/sca/trace/sampling.py
@@ -20,8 +20,15 @@ def downsample_average(trace: Trace, factor: int = 2) -> Trace:
:param factor:
:return:
"""
- resized = np.resize(trace.samples, len(trace.samples) - (len(trace.samples) % factor))
- result_samples = cast(np.ndarray, resized.reshape(-1, factor).mean(axis=1).astype(trace.samples.dtype, copy=False))
+ resized = np.resize(
+ trace.samples, len(trace.samples) - (len(trace.samples) % factor)
+ )
+ result_samples = cast(
+ np.ndarray,
+ resized.reshape(-1, factor)
+ .mean(axis=1)
+ .astype(trace.samples.dtype, copy=False),
+ )
return trace.with_samples(result_samples)
@@ -49,8 +56,13 @@ def downsample_max(trace: Trace, factor: int = 2) -> Trace:
:param factor:
:return:
"""
- resized = np.resize(trace.samples, len(trace.samples) - (len(trace.samples) % factor))
- result_samples = cast(np.ndarray, resized.reshape(-1, factor).max(axis=1).astype(trace.samples.dtype, copy=False))
+ resized = np.resize(
+ trace.samples, len(trace.samples) - (len(trace.samples) % factor)
+ )
+ result_samples = cast(
+ np.ndarray,
+ resized.reshape(-1, factor).max(axis=1).astype(trace.samples.dtype, copy=False),
+ )
return trace.with_samples(result_samples)
@@ -64,8 +76,13 @@ def downsample_min(trace: Trace, factor: int = 2) -> Trace:
:param factor:
:return:
"""
- resized = np.resize(trace.samples, len(trace.samples) - (len(trace.samples) % factor))
- result_samples = cast(np.ndarray, resized.reshape(-1, factor).min(axis=1).astype(trace.samples.dtype, copy=False))
+ resized = np.resize(
+ trace.samples, len(trace.samples) - (len(trace.samples) % factor)
+ )
+ result_samples = cast(
+ np.ndarray,
+ resized.reshape(-1, factor).min(axis=1).astype(trace.samples.dtype, copy=False),
+ )
return trace.with_samples(result_samples)
diff --git a/pyecsca/sca/trace/test.py b/pyecsca/sca/trace/test.py
index 8512a70..f90498a 100644
--- a/pyecsca/sca/trace/test.py
+++ b/pyecsca/sca/trace/test.py
@@ -12,8 +12,9 @@ from .combine import average_and_variance
from .edit import trim
-def _ttest_func(first_set: Sequence[Trace], second_set: Sequence[Trace],
- equal_var: bool) -> Optional[CombinedTrace]:
+def _ttest_func(
+ first_set: Sequence[Trace], second_set: Sequence[Trace], equal_var: bool
+) -> Optional[CombinedTrace]:
if not first_set or not second_set or len(first_set) == 0 or len(second_set) == 0:
return None
first_stack = np.stack([first.samples for first in first_set])
@@ -23,7 +24,12 @@ def _ttest_func(first_set: Sequence[Trace], second_set: Sequence[Trace],
@public
-def welch_ttest(first_set: Sequence[Trace], second_set: Sequence[Trace], dof: bool = False, p_value: bool = False) -> Optional[Tuple[CombinedTrace, ...]]:
+def welch_ttest(
+ first_set: Sequence[Trace],
+ second_set: Sequence[Trace],
+ dof: bool = False,
+ 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`.
Useful for Test Vector Leakage Analysis (TVLA).
@@ -51,8 +57,8 @@ def welch_ttest(first_set: Sequence[Trace], second_set: Sequence[Trace], dof: bo
tval = (mean_0.samples - mean_1.samples) / np.sqrt(varn_0 + varn_1)
result = [CombinedTrace(tval)]
if dof or p_value:
- top = (varn_0 + varn_1)**2
- bot = (varn_0**2 / (n0 - 1)) + (varn_1**2 / (n1 - 1))
+ top = (varn_0 + varn_1) ** 2
+ bot = (varn_0 ** 2 / (n0 - 1)) + (varn_1 ** 2 / (n1 - 1))
df = top / bot
del top
del bot
@@ -66,8 +72,9 @@ def welch_ttest(first_set: Sequence[Trace], second_set: Sequence[Trace], dof: bo
@public
-def student_ttest(first_set: Sequence[Trace], second_set: Sequence[Trace]) -> Optional[
- CombinedTrace]:
+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`.
Useful for Test Vector Leakage Analysis (TVLA).
@@ -80,7 +87,9 @@ def student_ttest(first_set: Sequence[Trace], second_set: Sequence[Trace]) -> Op
@public
-def ks_test(first_set: Sequence[Trace], second_set: Sequence[Trace]) -> Optional[CombinedTrace]:
+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`.
diff --git a/pyecsca/sca/trace/trace.py b/pyecsca/sca/trace/trace.py
index aef0837..96f1ec8 100644
--- a/pyecsca/sca/trace/trace.py
+++ b/pyecsca/sca/trace/trace.py
@@ -13,10 +13,13 @@ from public import public
@public
class Trace(object):
"""A trace, which has some samples and metadata."""
+
meta: Mapping[str, Any]
samples: ndarray
- def __init__(self, samples: ndarray, meta: Mapping[str, Any] = None, trace_set: Any = None):
+ def __init__(
+ self, samples: ndarray, meta: Mapping[str, Any] = None, trace_set: Any = None
+ ):
"""
Construct a new trace.
@@ -92,8 +95,11 @@ class Trace(object):
return Trace(copy(self.samples), copy(self.meta), copy(self.trace_set))
def __deepcopy__(self, memodict={}):
- return Trace(deepcopy(self.samples, memo=memodict), deepcopy(self.meta, memo=memodict),
- deepcopy(self.trace_set, memo=memodict))
+ return Trace(
+ deepcopy(self.samples, memo=memodict),
+ deepcopy(self.meta, memo=memodict),
+ deepcopy(self.trace_set, memo=memodict),
+ )
def __repr__(self):
return f"Trace(samples={self.samples!r}, trace_set={self.trace_set!r})"
@@ -103,8 +109,13 @@ class Trace(object):
class CombinedTrace(Trace):
"""A trace that was combined from other traces, `parents`."""
- def __init__(self, samples: ndarray, meta: Mapping[str, Any] = None, trace_set: Any = None,
- parents: Sequence[Trace] = None):
+ def __init__(
+ self,
+ samples: ndarray,
+ meta: Mapping[str, Any] = None,
+ trace_set: Any = None,
+ parents: Sequence[Trace] = None,
+ ):
super().__init__(samples, meta, trace_set=trace_set)
self.parents = None
if parents is not None:
diff --git a/pyecsca/sca/trace_set/base.py b/pyecsca/sca/trace_set/base.py
index 6f4e4b0..097d666 100644
--- a/pyecsca/sca/trace_set/base.py
+++ b/pyecsca/sca/trace_set/base.py
@@ -12,6 +12,7 @@ from ..trace import Trace
@public
class TraceSet(object):
"""A set of traces with some metadata."""
+
_traces: List[Trace]
_keys: List
@@ -46,6 +47,7 @@ class TraceSet(object):
raise NotImplementedError
def __repr__(self):
- args = ", ".join(["{}={!r}".format(key, getattr(self, key)) for key in
- self._keys])
+ args = ", ".join(
+ ["{}={!r}".format(key, getattr(self, key)) for key in self._keys]
+ )
return "TraceSet({})".format(args)
diff --git a/pyecsca/sca/trace_set/chipwhisperer.py b/pyecsca/sca/trace_set/chipwhisperer.py
index ba78a85..a56e676 100644
--- a/pyecsca/sca/trace_set/chipwhisperer.py
+++ b/pyecsca/sca/trace_set/chipwhisperer.py
@@ -24,7 +24,9 @@ class ChipWhispererTraceSet(TraceSet):
raise ValueError
@classmethod
- def inplace(cls, input: Union[str, Path, bytes, BinaryIO]) -> "ChipWhispererTraceSet":
+ def inplace(
+ cls, input: Union[str, Path, bytes, BinaryIO]
+ ) -> "ChipWhispererTraceSet":
raise NotImplementedError
def write(self, output: Union[str, Path, BinaryIO]):
@@ -39,10 +41,12 @@ class ChipWhispererTraceSet(TraceSet):
name = file_name[7:-4]
data = ChipWhispererTraceSet.__read_data(path, name)
traces = []
- for samples, key, textin, textout in zip_longest(data["traces"], data["keylist"],
- data["textin"], data["textout"]):
+ for samples, key, textin, textout in zip_longest(
+ data["traces"], data["keylist"], data["textin"], data["textout"]
+ ):
traces.append(
- Trace(samples, {"key": key, "textin": textin, "textout": textout}))
+ Trace(samples, {"key": key, "textin": textin, "textout": textout})
+ )
del data["traces"]
del data["keylist"]
del data["textin"]
@@ -52,7 +56,13 @@ class ChipWhispererTraceSet(TraceSet):
@classmethod
def __read_data(cls, path, name):
- types = {"keylist": None, "knownkey": None, "textin": None, "textout": None, "traces": None}
+ types = {
+ "keylist": None,
+ "knownkey": None,
+ "textin": None,
+ "textout": None,
+ "traces": None,
+ }
for type in types.keys():
type_path = join(path, name + type + ".npy")
if exists(type_path) and isfile(type_path):
diff --git a/pyecsca/sca/trace_set/hdf5.py b/pyecsca/sca/trace_set/hdf5.py
index 4b3b7b0..f1d34ef 100644
--- a/pyecsca/sca/trace_set/hdf5.py
+++ b/pyecsca/sca/trace_set/hdf5.py
@@ -22,6 +22,7 @@ from .. import Trace
@public
class HDF5Meta(MutableMapping):
"""Metadata mapping that is HDF5-compatible (items are picklable)."""
+
_dataset: h5py.AttributeManager
def __init__(self, attrs: h5py.AttributeManager):
@@ -55,12 +56,18 @@ class HDF5Meta(MutableMapping):
@public
class HDF5TraceSet(TraceSet):
"""A traceset based on the HDF5 (Hierarchical Data Format)."""
+
_file: Optional[h5py.File]
_ordering: List[str]
# _meta: Optional[HDF5Meta]
- def __init__(self, *traces: Trace, _file: Optional[h5py.File] = None,
- _ordering: Optional[List[str]] = None, **kwargs):
+ def __init__(
+ self,
+ *traces: Trace,
+ _file: Optional[h5py.File] = None,
+ _ordering: Optional[List[str]] = None,
+ **kwargs,
+ ):
# self._meta = HDF5Meta(_file.attrs) if _file is not None else None
self._file = _file
if _ordering is None:
@@ -76,7 +83,9 @@ class HDF5TraceSet(TraceSet):
else:
raise TypeError
kwargs = dict(hdf5.attrs)
- kwargs["_ordering"] = list(kwargs["_ordering"]) if "_ordering" in kwargs else list(hdf5.keys())
+ kwargs["_ordering"] = (
+ list(kwargs["_ordering"]) if "_ordering" in kwargs else list(hdf5.keys())
+ )
traces = []
for k in kwargs["_ordering"]:
meta = dict(HDF5Meta(hdf5[k].attrs))
@@ -94,7 +103,9 @@ class HDF5TraceSet(TraceSet):
else:
raise TypeError
kwargs = dict(hdf5.attrs)
- kwargs["_ordering"] = list(kwargs["_ordering"]) if "_ordering" in kwargs else list(hdf5.keys())
+ kwargs["_ordering"] = (
+ list(kwargs["_ordering"]) if "_ordering" in kwargs else list(hdf5.keys())
+ )
traces = []
for k in kwargs["_ordering"]:
meta = HDF5Meta(hdf5[k].attrs)
@@ -182,5 +193,11 @@ class HDF5TraceSet(TraceSet):
fname = self._file.filename
else:
status = "(closed)"
- args = ", ".join([f"{key}={getattr(self, key)!r}" for key in self._keys if not key.startswith("_")])
+ args = ", ".join(
+ [
+ f"{key}={getattr(self, key)!r}"
+ for key in self._keys
+ if not key.startswith("_")
+ ]
+ )
return f"HDF5TraceSet('{fname}'{status}, {args})"
diff --git a/pyecsca/sca/trace_set/inspector.py b/pyecsca/sca/trace_set/inspector.py
index cc3f81f..83bbade 100644
--- a/pyecsca/sca/trace_set/inspector.py
+++ b/pyecsca/sca/trace_set/inspector.py
@@ -25,7 +25,7 @@ class SampleCoding(IntEnum):
def dtype(self):
char = "f" if self.value & 0x10 else "i"
- return np.dtype("<{}{}".format(char, self.value & 0x0f))
+ return np.dtype("<{}{}".format(char, self.value & 0x0F))
@public
@@ -107,37 +107,44 @@ class InspectorTraceSet(TraceSet):
_tag_parsers: dict = {
0x41: ("num_traces", 4, Parsers.read_int, Parsers.write_int),
0x42: ("num_samples", 4, Parsers.read_int, Parsers.write_int),
- 0x43: ("sample_coding", 1,
- lambda bytes: SampleCoding(Parsers.read_int(bytes)),
- lambda coding, length: Parsers.write_int(coding.value,
- length=length)),
+ 0x43: (
+ "sample_coding",
+ 1,
+ lambda bytes: SampleCoding(Parsers.read_int(bytes)),
+ lambda coding, length: Parsers.write_int(coding.value, length=length),
+ ),
0x44: ("data_space", 2, Parsers.read_int, Parsers.write_int),
0x45: ("title_space", 1, Parsers.read_int, Parsers.write_int),
0x46: ("global_title", None, Parsers.read_str, Parsers.write_str),
0x47: ("description", None, Parsers.read_str, Parsers.write_str),
0x48: ("x_offset", None, Parsers.read_int, Parsers.write_int),
0x49: ("x_label", None, Parsers.read_str, Parsers.write_str),
- 0x4a: ("y_label", None, Parsers.read_str, Parsers.write_str),
- 0x4b: ("x_scale", 4, Parsers.read_float, Parsers.write_float),
- 0x4c: ("y_scale", 4, Parsers.read_float, Parsers.write_float),
- 0x4d: ("trace_offset", 4, Parsers.read_int, Parsers.write_int),
- 0x4e: ("log_scale", 1, Parsers.read_int, Parsers.write_int),
+ 0x4A: ("y_label", None, Parsers.read_str, Parsers.write_str),
+ 0x4B: ("x_scale", 4, Parsers.read_float, Parsers.write_float),
+ 0x4C: ("y_scale", 4, Parsers.read_float, Parsers.write_float),
+ 0x4D: ("trace_offset", 4, Parsers.read_int, Parsers.write_int),
+ 0x4E: ("log_scale", 1, Parsers.read_int, Parsers.write_int),
0x55: ("scope_range", 4, Parsers.read_float, Parsers.write_float),
0x56: ("scope_coupling", 4, Parsers.read_int, Parsers.write_int),
0x57: ("scope_offset", 4, Parsers.read_float, Parsers.write_float),
0x58: ("scope_impedance", 4, Parsers.read_float, Parsers.write_float),
0x59: ("scope_id", None, Parsers.read_str, Parsers.write_str),
- 0x5a: ("filter_type", 4, Parsers.read_int, Parsers.write_int),
- 0x5b: ("filter_frequency", 4, Parsers.read_float, Parsers.write_float),
- 0x5c: ("filter_range", 4, Parsers.read_float, Parsers.read_float),
+ 0x5A: ("filter_type", 4, Parsers.read_int, Parsers.write_int),
+ 0x5B: ("filter_frequency", 4, Parsers.read_float, Parsers.write_float),
+ 0x5C: ("filter_range", 4, Parsers.read_float, Parsers.read_float),
0x60: ("external_clock", 1, Parsers.read_bool, Parsers.write_bool),
0x61: ("external_clock_threshold", 4, Parsers.read_float, Parsers.write_float),
0x62: ("external_clock_multiplier", 4, Parsers.read_int, Parsers.write_int),
0x63: ("external_clock_phase_shift", 4, Parsers.read_int, Parsers.write_int),
0x64: ("external_clock_resampler_mask", 4, Parsers.read_int, Parsers.write_int),
- 0x65: ("external_clock_resampler_enabled", 1, Parsers.read_bool, Parsers.write_bool),
+ 0x65: (
+ "external_clock_resampler_enabled",
+ 1,
+ Parsers.read_bool,
+ Parsers.write_bool,
+ ),
0x66: ("external_clock_frequency", 4, Parsers.read_float, Parsers.write_float),
- 0x67: ("external_clock_time_base", 4, Parsers.read_int, Parsers.write_int)
+ 0x67: ("external_clock_time_base", 4, Parsers.read_int, Parsers.write_int),
}
@classmethod
@@ -171,28 +178,33 @@ class InspectorTraceSet(TraceSet):
tag = ord(file.read(1))
length = ord(file.read(1))
if length & 0x80:
- length = Parsers.read_int(file.read(length & 0x7f))
+ length = Parsers.read_int(file.read(length & 0x7F))
value = file.read(length)
if tag in InspectorTraceSet._tag_parsers:
tag_name, tag_len, tag_reader, _ = InspectorTraceSet._tag_parsers[tag]
if tag_len is None or length == tag_len:
tags[tag_name] = tag_reader(value)
- elif tag == 0x5f and length == 0:
+ elif tag == 0x5F and length == 0:
break
else:
continue
result = []
for _ in range(tags["num_traces"]):
- title = None if "title_space" not in tags else Parsers.read_str(
- file.read(tags["title_space"]))
+ title = (
+ None
+ if "title_space" not in tags
+ else Parsers.read_str(file.read(tags["title_space"]))
+ )
data = None if "data_space" not in tags else file.read(tags["data_space"])
dtype = tags["sample_coding"].dtype()
try:
samples = np.fromfile(file, dtype, tags["num_samples"])
except UnsupportedOperation:
samples = np.frombuffer(
- file.read(dtype.itemsize * tags["num_samples"]), dtype,
- tags["num_samples"])
+ file.read(dtype.itemsize * tags["num_samples"]),
+ dtype,
+ tags["num_samples"],
+ )
result.append(Trace(samples, {"title": title, "data": data}))
return result, tags
@@ -222,12 +234,13 @@ class InspectorTraceSet(TraceSet):
tag_byte = Parsers.write_int(tag, length=1)
value_bytes = tag_writer(getattr(self, tag_name), tag_len)
length = len(value_bytes)
- if length <= 0x7f:
+ if length <= 0x7F:
length_bytes = Parsers.write_int(length, length=1)
else:
- length_data = Parsers.write_int(length, length=(length.bit_length() + 7) // 8)
- length_bytes = Parsers.write_int(
- 0x80 | len(length_data)) + length_data
+ length_data = Parsers.write_int(
+ length, length=(length.bit_length() + 7) // 8
+ )
+ length_bytes = Parsers.write_int(0x80 | len(length_data)) + length_data
file.write(tag_byte)
file.write(length_bytes)
file.write(value_bytes)
@@ -238,7 +251,9 @@ class InspectorTraceSet(TraceSet):
file.write(Parsers.write_str(trace.meta["title"]))
if self.data_space != 0 and trace.meta["data"] is not None:
file.write(trace.meta["data"])
- unscaled = InspectorTraceSet.__unscale(trace.samples, self.y_scale, self.sample_coding)
+ unscaled = InspectorTraceSet.__unscale(
+ trace.samples, self.y_scale, self.sample_coding
+ )
try:
unscaled.tofile(file)
except UnsupportedOperation:
diff --git a/setup.py b/setup.py
index 0ece313..1f7b610 100644
--- a/setup.py
+++ b/setup.py
@@ -48,7 +48,7 @@ setup(
"chipwhisperer": ["chipwhisperer"],
"smartcard": ["pyscard"],
"gmp": ["gmpy2"],
- "dev": ["mypy", "flake8", "interrogate", "pyinstrument"],
+ "dev": ["mypy", "flake8", "interrogate", "pyinstrument", "black"],
"test": ["nose2", "parameterized", "coverage"],
"doc": ["sphinx", "sphinx-autodoc-typehints", "nbsphinx"]
}
diff --git a/test/ec/perf_formula.py b/test/ec/perf_formula.py
index 9e651ae..2cc4513 100755
--- a/test/ec/perf_formula.py
+++ b/test/ec/perf_formula.py
@@ -9,9 +9,20 @@ from utils import Profiler
@click.command()
@click.option("-p", "--profiler", type=click.Choice(("py", "c")), default="py")
-@click.option("-m", "--mod", type=click.Choice(("python", "gmp")), default="gmp" if has_gmp else "python")
+@click.option(
+ "-m",
+ "--mod",
+ type=click.Choice(("python", "gmp")),
+ default="gmp" if has_gmp else "python",
+)
@click.option("-o", "--operations", type=click.INT, default=5000)
-@click.option("-d", "--directory", type=click.Path(file_okay=False, dir_okay=True), default=None, envvar="DIR")
+@click.option(
+ "-d",
+ "--directory",
+ type=click.Path(file_okay=False, dir_okay=True),
+ default=None,
+ envvar="DIR",
+)
def main(profiler, mod, operations, directory):
with TemporaryConfig() as cfg:
cfg.ec.mod_implementation = mod
@@ -19,22 +30,36 @@ def main(profiler, mod, operations, directory):
coords = p256.curve.coordinate_model
add = coords.formulas["add-2016-rcb"]
dbl = coords.formulas["dbl-2016-rcb"]
- click.echo(f"Profiling {operations} {p256.curve.prime.bit_length()}-bit doubling formula executions...")
+ click.echo(
+ f"Profiling {operations} {p256.curve.prime.bit_length()}-bit doubling formula executions..."
+ )
one_point = p256.generator
- with Profiler(profiler, directory, f"formula_dbl2016rcb_p256_{operations}_{mod}"):
+ with Profiler(
+ profiler, directory, f"formula_dbl2016rcb_p256_{operations}_{mod}"
+ ):
for _ in range(operations):
one_point = dbl(p256.curve.prime, one_point, **p256.curve.parameters)[0]
- click.echo(f"Profiling {operations} {p256.curve.prime.bit_length()}-bit addition formula executions...")
+ click.echo(
+ f"Profiling {operations} {p256.curve.prime.bit_length()}-bit addition formula executions..."
+ )
other_point = p256.generator
- with Profiler(profiler, directory, f"formula_add2016rcb_p256_{operations}_{mod}"):
+ with Profiler(
+ profiler, directory, f"formula_add2016rcb_p256_{operations}_{mod}"
+ ):
for _ in range(operations):
- one_point = add(p256.curve.prime, one_point, other_point, **p256.curve.parameters)[0]
+ one_point = add(
+ p256.curve.prime, one_point, other_point, **p256.curve.parameters
+ )[0]
ed25519 = get_params("other", "Ed25519", "extended")
ecoords = ed25519.curve.coordinate_model
dblg = ecoords.formulas["mdbl-2008-hwcd"]
- click.echo(f"Profiling {operations} {ed25519.curve.prime.bit_length()}-bit doubling formula executions (with assumption)...")
+ click.echo(
+ f"Profiling {operations} {ed25519.curve.prime.bit_length()}-bit doubling formula executions (with assumption)..."
+ )
eone_point = ed25519.generator
- with Profiler(profiler, directory, f"formula_mdbl2008hwcd_ed25519_{operations}_{mod}"):
+ with Profiler(
+ profiler, directory, f"formula_mdbl2008hwcd_ed25519_{operations}_{mod}"
+ ):
for _ in range(operations):
dblg(ed25519.curve.prime, eone_point, **ed25519.curve.parameters)
diff --git a/test/ec/perf_mod.py b/test/ec/perf_mod.py
index 37cf41b..49fe9a7 100755
--- a/test/ec/perf_mod.py
+++ b/test/ec/perf_mod.py
@@ -8,20 +8,33 @@ from utils import Profiler
@click.command()
@click.option("-p", "--profiler", type=click.Choice(("py", "c")), default="py")
-@click.option("-m", "--mod", type=click.Choice(("python", "gmp")), default="gmp" if has_gmp else "python")
+@click.option(
+ "-m",
+ "--mod",
+ type=click.Choice(("python", "gmp")),
+ default="gmp" if has_gmp else "python",
+)
@click.option("-o", "--operations", type=click.INT, default=100000)
-@click.option("-d", "--directory", type=click.Path(file_okay=False, dir_okay=True), default=None, envvar="DIR")
+@click.option(
+ "-d",
+ "--directory",
+ type=click.Path(file_okay=False, dir_okay=True),
+ default=None,
+ envvar="DIR",
+)
def main(profiler, mod, operations, directory):
with TemporaryConfig() as cfg:
cfg.ec.mod_implementation = mod
- n = 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff
+ n = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF
a = Mod(0x11111111111111111111111111111111, n)
- b = Mod(0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, n)
+ b = Mod(0xBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB, n)
click.echo(f"Profiling {operations} {n.bit_length()}-bit modular inverse...")
with Profiler(profiler, directory, f"mod_256b_inverse_{operations}_{mod}"):
for _ in range(operations):
a.inverse()
- click.echo(f"Profiling {operations} {n.bit_length()}-bit modular square root...")
+ click.echo(
+ f"Profiling {operations} {n.bit_length()}-bit modular square root..."
+ )
with Profiler(profiler, directory, f"mod_256b_sqrt_{operations}_{mod}"):
for _ in range(operations):
a.sqrt()
@@ -30,16 +43,20 @@ def main(profiler, mod, operations, directory):
with Profiler(profiler, directory, f"mod_256b_multiply_{operations}_{mod}"):
for _ in range(operations):
c = c * b
- click.echo(f"Profiling {operations} {n.bit_length()}-bit constant modular multiply...")
+ click.echo(
+ f"Profiling {operations} {n.bit_length()}-bit constant modular multiply..."
+ )
c = a
- with Profiler(profiler, directory, f"mod_256b_constmultiply_{operations}_{mod}"):
+ with Profiler(
+ profiler, directory, f"mod_256b_constmultiply_{operations}_{mod}"
+ ):
for _ in range(operations):
c = c * 48006
click.echo(f"Profiling {operations} {n.bit_length()}-bit modular square...")
c = a
with Profiler(profiler, directory, f"mod_256b_square_{operations}_{mod}"):
for _ in range(operations):
- c = c**2
+ c = c ** 2
click.echo(f"Profiling {operations} {n.bit_length()}-bit modular add...")
c = a
with Profiler(profiler, directory, f"mod_256b_add_{operations}_{mod}"):
@@ -50,7 +67,9 @@ def main(profiler, mod, operations, directory):
with Profiler(profiler, directory, f"mod_256b_subtract_{operations}_{mod}"):
for _ in range(operations):
c = c - b
- click.echo(f"Profiling {operations} {n.bit_length()}-bit modular quadratic residue checks...")
+ click.echo(
+ f"Profiling {operations} {n.bit_length()}-bit modular quadratic residue checks..."
+ )
with Profiler(profiler, directory, f"mod_256b_isresidue_{operations}_{mod}"):
for _ in range(operations):
a.is_residue()
diff --git a/test/ec/perf_mult.py b/test/ec/perf_mult.py
index 2ec82b0..36f004b 100755
--- a/test/ec/perf_mult.py
+++ b/test/ec/perf_mult.py
@@ -10,9 +10,20 @@ from utils import Profiler
@click.command()
@click.option("-p", "--profiler", type=click.Choice(("py", "c")), default="py")
-@click.option("-m", "--mod", type=click.Choice(("python", "gmp")), default="gmp" if has_gmp else "python")
+@click.option(
+ "-m",
+ "--mod",
+ type=click.Choice(("python", "gmp")),
+ default="gmp" if has_gmp else "python",
+)
@click.option("-o", "--operations", type=click.INT, default=50)
-@click.option("-d", "--directory", type=click.Path(file_okay=False, dir_okay=True), default=None, envvar="DIR")
+@click.option(
+ "-d",
+ "--directory",
+ type=click.Path(file_okay=False, dir_okay=True),
+ default=None,
+ envvar="DIR",
+)
def main(profiler, mod, operations, directory):
with TemporaryConfig() as cfg:
cfg.ec.mod_implementation = mod
@@ -21,12 +32,16 @@ def main(profiler, mod, operations, directory):
add = coords.formulas["add-2016-rcb"]
dbl = coords.formulas["dbl-2016-rcb"]
mult = LTRMultiplier(add, dbl)
- click.echo(f"Profiling {operations} {p256.curve.prime.bit_length()}-bit scalar multiplication executions...")
+ click.echo(
+ f"Profiling {operations} {p256.curve.prime.bit_length()}-bit scalar multiplication executions..."
+ )
one_point = p256.generator
with Profiler(profiler, directory, f"mult_ltr_rcb_p256_{operations}_{mod}"):
for _ in range(operations):
mult.init(p256, one_point)
- one_point = mult.multiply(0x71a55e0c1abb3a0e069419e0f837bc195f1b9545e69fc51e53c4d48d7fea3b1a)
+ one_point = mult.multiply(
+ 0x71A55E0C1ABB3A0E069419E0F837BC195F1B9545E69FC51E53C4D48D7FEA3B1A
+ )
# ed25519 = get_params("other", "Ed25519", "extended")
# ecoords = ed25519.curve.coordinate_model
# dblg = ecoords.formulas["mdbl-2008-hwcd"]
diff --git a/test/ec/test_configuration.py b/test/ec/test_configuration.py
index 9c8e361..7603321 100644
--- a/test/ec/test_configuration.py
+++ b/test/ec/test_configuration.py
@@ -1,14 +1,20 @@
from unittest import TestCase
-from pyecsca.ec.configuration import (all_configurations, HashType, RandomMod, Multiplication,
- Squaring, Reduction, Inversion)
+from pyecsca.ec.configuration import (
+ all_configurations,
+ HashType,
+ RandomMod,
+ Multiplication,
+ Squaring,
+ Reduction,
+ Inversion,
+)
from pyecsca.ec.model import ShortWeierstrassModel
from pyecsca.ec.mult import LTRMultiplier
from .utils import slow
class ConfigurationTests(TestCase):
-
def base_independents(self):
return {
"hash_type": HashType.SHA1,
@@ -16,7 +22,7 @@ class ConfigurationTests(TestCase):
"mult": Multiplication.BASE,
"sqr": Squaring.BASE,
"red": Reduction.BASE,
- "inv": Inversion.GCD
+ "inv": Inversion.GCD,
}
@slow
@@ -29,15 +35,23 @@ class ConfigurationTests(TestCase):
def test_weierstrass_projective(self):
model = ShortWeierstrassModel()
coords = model.coordinates["projective"]
- configs = list(all_configurations(model=model, coords=coords, **self.base_independents()))
+ configs = list(
+ all_configurations(model=model, coords=coords, **self.base_independents())
+ )
self.assertEqual(len(configs), 1960)
def test_mult_class(self):
model = ShortWeierstrassModel()
coords = model.coordinates["projective"]
scalarmult = LTRMultiplier
- configs = list(all_configurations(model=model, coords=coords, scalarmult=scalarmult,
- **self.base_independents()))
+ configs = list(
+ all_configurations(
+ model=model,
+ coords=coords,
+ scalarmult=scalarmult,
+ **self.base_independents()
+ )
+ )
self.assertEqual(len(configs), 560)
def test_one(self):
@@ -50,16 +64,37 @@ class ConfigurationTests(TestCase):
"scl": None,
"always": True,
"complete": False,
- "short_circuit": True
+ "short_circuit": True,
}
- configs = list(all_configurations(model=model, coords=coords, scalarmult=scalarmult,
- **self.base_independents()))
+ configs = list(
+ all_configurations(
+ model=model,
+ coords=coords,
+ scalarmult=scalarmult,
+ **self.base_independents()
+ )
+ )
self.assertEqual(len(configs), 1)
- scalarmult = LTRMultiplier(coords.formulas["add-1998-cmo"], coords.formulas["dbl-1998-cmo"],
- None, True, False, True)
- configs = list(all_configurations(model=model, coords=coords, scalarmult=scalarmult,
- **self.base_independents()))
+ scalarmult = LTRMultiplier(
+ coords.formulas["add-1998-cmo"],
+ coords.formulas["dbl-1998-cmo"],
+ None,
+ True,
+ False,
+ True,
+ )
+ configs = list(
+ all_configurations(
+ model=model,
+ coords=coords,
+ scalarmult=scalarmult,
+ **self.base_independents()
+ )
+ )
self.assertEqual(len(configs), 1)
- configs = list(all_configurations(model=model, scalarmult=scalarmult,
- **self.base_independents()))
+ configs = list(
+ all_configurations(
+ model=model, scalarmult=scalarmult, **self.base_independents()
+ )
+ )
self.assertEqual(len(configs), 1)
diff --git a/test/ec/test_context.py b/test/ec/test_context.py
index 603369d..04dbc7d 100644
--- a/test/ec/test_context.py
+++ b/test/ec/test_context.py
@@ -1,7 +1,15 @@
from unittest import TestCase
-from pyecsca.ec.context import (local, DefaultContext, NullContext, getcontext,
- setcontext, resetcontext, Tree, PathContext)
+from pyecsca.ec.context import (
+ local,
+ DefaultContext,
+ NullContext,
+ getcontext,
+ setcontext,
+ resetcontext,
+ Tree,
+ PathContext,
+)
from pyecsca.ec.key_generation import KeygenAction, KeyGeneration
from pyecsca.ec.params import get_params
from pyecsca.ec.mod import RandomModAction
@@ -9,7 +17,6 @@ from pyecsca.ec.mult import LTRMultiplier, ScalarMultiplicationAction
class TreeTests(TestCase):
-
def test_walk_by_key(self):
tree = Tree()
tree["a"] = Tree()
@@ -46,14 +53,16 @@ class TreeTests(TestCase):
class ContextTests(TestCase):
-
def setUp(self):
self.secp128r1 = get_params("secg", "secp128r1", "projective")
self.base = self.secp128r1.generator
self.coords = self.secp128r1.curve.coordinate_model
- self.mult = LTRMultiplier(self.coords.formulas["add-1998-cmo"],
- self.coords.formulas["dbl-1998-cmo"], self.coords.formulas["z"],
- always=True)
+ self.mult = LTRMultiplier(
+ self.coords.formulas["add-1998-cmo"],
+ self.coords.formulas["dbl-1998-cmo"],
+ self.coords.formulas["z"],
+ always=True,
+ )
self.mult.init(self.secp128r1, self.base)
def test_null(self):
diff --git a/test/ec/test_curve.py b/test/ec/test_curve.py
index c358c68..345d3de 100644
--- a/test/ec/test_curve.py
+++ b/test/ec/test_curve.py
@@ -19,55 +19,89 @@ class CurveTests(TestCase):
def test_init(self):
with self.assertRaises(ValueError):
- EllipticCurve(MontgomeryModel(), self.secp128r1.curve.coordinate_model, 1,
- InfinityPoint(self.secp128r1.curve.coordinate_model), parameters={})
+ EllipticCurve(
+ MontgomeryModel(),
+ self.secp128r1.curve.coordinate_model,
+ 1,
+ InfinityPoint(self.secp128r1.curve.coordinate_model),
+ parameters={},
+ )
with self.assertRaises(ValueError):
- EllipticCurve(self.secp128r1.curve.model, self.secp128r1.curve.coordinate_model, 15,
- InfinityPoint(self.secp128r1.curve.coordinate_model), parameters={"c": 0})
+ EllipticCurve(
+ self.secp128r1.curve.model,
+ self.secp128r1.curve.coordinate_model,
+ 15,
+ InfinityPoint(self.secp128r1.curve.coordinate_model),
+ parameters={"c": 0},
+ )
with self.assertRaises(ValueError):
- EllipticCurve(self.secp128r1.curve.model, self.secp128r1.curve.coordinate_model, 15,
- InfinityPoint(self.secp128r1.curve.coordinate_model),
- parameters={"a": Mod(1, 5), "b": Mod(2, 5)})
+ EllipticCurve(
+ self.secp128r1.curve.model,
+ self.secp128r1.curve.coordinate_model,
+ 15,
+ InfinityPoint(self.secp128r1.curve.coordinate_model),
+ parameters={"a": Mod(1, 5), "b": Mod(2, 5)},
+ )
def test_is_neutral(self):
- self.assertTrue(self.secp128r1.curve.is_neutral(InfinityPoint(self.secp128r1.curve.coordinate_model)))
+ self.assertTrue(
+ self.secp128r1.curve.is_neutral(
+ InfinityPoint(self.secp128r1.curve.coordinate_model)
+ )
+ )
def test_is_on_curve(self):
self.assertTrue(self.secp128r1.curve.is_on_curve(self.secp128r1.curve.neutral))
- pt = Point(self.secp128r1.curve.coordinate_model,
- X=Mod(0x161ff7528b899b2d0c28607ca52c5b86, self.secp128r1.curve.prime),
- Y=Mod(0xcf5ac8395bafeb13c02da292dded7a83, self.secp128r1.curve.prime),
- Z=Mod(1, self.secp128r1.curve.prime))
+ pt = Point(
+ self.secp128r1.curve.coordinate_model,
+ X=Mod(0x161FF7528B899B2D0C28607CA52C5B86, self.secp128r1.curve.prime),
+ Y=Mod(0xCF5AC8395BAFEB13C02DA292DDED7A83, self.secp128r1.curve.prime),
+ Z=Mod(1, self.secp128r1.curve.prime),
+ )
self.assertTrue(self.secp128r1.curve.is_on_curve(pt))
self.assertTrue(self.secp128r1.curve.is_on_curve(pt.to_affine()))
- other = Point(self.secp128r1.curve.coordinate_model,
- X=Mod(0x161ff7528b899b2d0c28607ca52c5b86, self.secp128r1.curve.prime),
- Y=Mod(0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, self.secp128r1.curve.prime),
- Z=Mod(1, self.secp128r1.curve.prime))
+ other = Point(
+ self.secp128r1.curve.coordinate_model,
+ X=Mod(0x161FF7528B899B2D0C28607CA52C5B86, self.secp128r1.curve.prime),
+ Y=Mod(0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, self.secp128r1.curve.prime),
+ Z=Mod(1, self.secp128r1.curve.prime),
+ )
self.assertFalse(self.secp128r1.curve.is_on_curve(other))
self.assertFalse(self.secp128r1.curve.is_on_curve(self.curve25519.generator))
def test_affine_add(self):
- pt = Point(AffineCoordinateModel(self.secp128r1.curve.model),
- x=Mod(0xeb916224eda4fb356421773573297c15, self.secp128r1.curve.prime),
- y=Mod(0xbcdaf32a2c08fd4271228fef35070848, self.secp128r1.curve.prime))
+ pt = Point(
+ AffineCoordinateModel(self.secp128r1.curve.model),
+ x=Mod(0xEB916224EDA4FB356421773573297C15, self.secp128r1.curve.prime),
+ y=Mod(0xBCDAF32A2C08FD4271228FEF35070848, self.secp128r1.curve.prime),
+ )
self.assertIsNotNone(self.secp128r1.curve.affine_add(self.affine_base, pt))
added = self.secp128r1.curve.affine_add(self.affine_base, self.affine_base)
doubled = self.secp128r1.curve.affine_double(self.affine_base)
self.assertEqual(added, doubled)
- self.assertEqual(self.secp128r1.curve.affine_add(self.secp128r1.curve.neutral, pt), pt)
- self.assertEqual(self.secp128r1.curve.affine_add(pt, self.secp128r1.curve.neutral), pt)
+ self.assertEqual(
+ self.secp128r1.curve.affine_add(self.secp128r1.curve.neutral, pt), pt
+ )
+ self.assertEqual(
+ self.secp128r1.curve.affine_add(pt, self.secp128r1.curve.neutral), pt
+ )
def test_affine_double(self):
self.assertIsNotNone(self.secp128r1.curve.affine_double(self.affine_base))
- self.assertEqual(self.secp128r1.curve.affine_double(self.secp128r1.curve.neutral), self.secp128r1.curve.neutral)
+ self.assertEqual(
+ self.secp128r1.curve.affine_double(self.secp128r1.curve.neutral),
+ self.secp128r1.curve.neutral,
+ )
def test_affine_negate(self):
self.assertIsNotNone(self.secp128r1.curve.affine_negate(self.affine_base))
- self.assertEqual(self.secp128r1.curve.affine_negate(self.secp128r1.curve.neutral), self.secp128r1.curve.neutral)
+ self.assertEqual(
+ self.secp128r1.curve.affine_negate(self.secp128r1.curve.neutral),
+ self.secp128r1.curve.neutral,
+ )
with self.assertRaises(ValueError):
self.secp128r1.curve.affine_negate(self.base)
with self.assertRaises(ValueError):
@@ -79,8 +113,13 @@ class CurveTests(TestCase):
expected = self.secp128r1.curve.affine_double(expected)
expected = self.secp128r1.curve.affine_add(expected, self.affine_base)
expected = self.secp128r1.curve.affine_double(expected)
- self.assertEqual(self.secp128r1.curve.affine_multiply(self.affine_base, 10), expected)
- self.assertEqual(self.secp128r1.curve.affine_multiply(self.secp128r1.curve.neutral, 10), self.secp128r1.curve.neutral)
+ self.assertEqual(
+ self.secp128r1.curve.affine_multiply(self.affine_base, 10), expected
+ )
+ self.assertEqual(
+ self.secp128r1.curve.affine_multiply(self.secp128r1.curve.neutral, 10),
+ self.secp128r1.curve.neutral,
+ )
with self.assertRaises(ValueError):
self.secp128r1.curve.affine_multiply(self.base, 10)
with self.assertRaises(ValueError):
@@ -129,7 +168,9 @@ class CurveTests(TestCase):
with self.assertRaises(ValueError):
affine_curve.decode_point(unhexlify("03161ff7528b899b2d0c28607ca52c5b"))
with self.assertRaises(ValueError):
- affine_curve.decode_point(unhexlify("04161ff7528b899b2d0c28607ca52c5b2c5b2c5b2c5b"))
+ affine_curve.decode_point(
+ unhexlify("04161ff7528b899b2d0c28607ca52c5b2c5b2c5b2c5b")
+ )
with self.assertRaises(ValueError):
affine_curve.decode_point(unhexlify("7a161ff7528b899b2d0c28607ca52c5b86"))
with self.assertRaises(ValueError):
diff --git a/test/ec/test_formula.py b/test/ec/test_formula.py
index ec585ad..ffae4c4 100644
--- a/test/ec/test_formula.py
+++ b/test/ec/test_formula.py
@@ -10,20 +10,25 @@ from pyecsca.ec.point import Point
class FormulaTests(TestCase):
-
def setUp(self):
self.secp128r1 = get_params("secg", "secp128r1", "projective")
self.add = self.secp128r1.curve.coordinate_model.formulas["add-2007-bl"]
self.dbl = self.secp128r1.curve.coordinate_model.formulas["dbl-2007-bl"]
self.mdbl = self.secp128r1.curve.coordinate_model.formulas["mdbl-2007-bl"]
self.jac_secp128r1 = get_params("secg", "secp128r1", "jacobian")
- self.jac_dbl = self.jac_secp128r1.curve.coordinate_model.formulas["dbl-1998-hnm"]
+ self.jac_dbl = self.jac_secp128r1.curve.coordinate_model.formulas[
+ "dbl-1998-hnm"
+ ]
def test_wrong_call(self):
with self.assertRaises(ValueError):
self.add(self.secp128r1.curve.prime)
with self.assertRaises(ValueError):
- self.add(self.secp128r1.curve.prime, self.secp128r1.generator.to_affine(), self.secp128r1.generator.to_affine())
+ self.add(
+ self.secp128r1.curve.prime,
+ self.secp128r1.generator.to_affine(),
+ self.secp128r1.generator.to_affine(),
+ )
def test_indices(self):
self.assertEqual(self.add.input_index, 1)
@@ -47,33 +52,56 @@ class FormulaTests(TestCase):
self.assertEqual(self.add.num_addsubs, 10)
def test_assumptions(self):
- res = self.mdbl(self.secp128r1.curve.prime, self.secp128r1.generator, **self.secp128r1.curve.parameters)
+ res = self.mdbl(
+ self.secp128r1.curve.prime,
+ self.secp128r1.generator,
+ **self.secp128r1.curve.parameters
+ )
self.assertIsNotNone(res)
- coords = {name: value * 5 for name, value in self.secp128r1.generator.coords.items()}
+ coords = {
+ name: value * 5 for name, value in self.secp128r1.generator.coords.items()
+ }
other = Point(self.secp128r1.generator.coordinate_model, **coords)
with self.assertRaises(UnsatisfiedAssumptionError):
- self.mdbl(self.secp128r1.curve.prime, other, **self.secp128r1.curve.parameters)
+ self.mdbl(
+ self.secp128r1.curve.prime, other, **self.secp128r1.curve.parameters
+ )
with TemporaryConfig() as cfg:
cfg.ec.unsatisfied_formula_assumption_action = "ignore"
- pt = self.mdbl(self.secp128r1.curve.prime, other, **self.secp128r1.curve.parameters)
+ pt = self.mdbl(
+ self.secp128r1.curve.prime, other, **self.secp128r1.curve.parameters
+ )
self.assertIsNotNone(pt)
def test_parameters(self):
- res = self.jac_dbl(self.secp128r1.curve.prime, self.jac_secp128r1.generator, **self.jac_secp128r1.curve.parameters)
+ res = self.jac_dbl(
+ self.secp128r1.curve.prime,
+ self.jac_secp128r1.generator,
+ **self.jac_secp128r1.curve.parameters
+ )
self.assertIsNotNone(res)
def test_symbolic(self):
p = self.secp128r1.curve.prime
k = FF(p)
coords = self.secp128r1.curve.coordinate_model
- sympy_params = {key: SymbolicMod(k(int(value)), p) for key, value in self.secp128r1.curve.parameters.items()}
- symbolic_point = Point(coords, **{key: SymbolicMod(symbols(key), p) for key in coords.variables})
+ sympy_params = {
+ key: SymbolicMod(k(int(value)), p)
+ for key, value in self.secp128r1.curve.parameters.items()
+ }
+ symbolic_point = Point(
+ coords, **{key: SymbolicMod(symbols(key), p) for key in coords.variables}
+ )
symbolic_double = self.dbl(p, symbolic_point, **sympy_params)[0]
- generator_double = self.dbl(p, self.secp128r1.generator, **self.secp128r1.curve.parameters)[0]
+ generator_double = self.dbl(
+ p, self.secp128r1.generator, **self.secp128r1.curve.parameters
+ )[0]
for outer_var in coords.variables:
symbolic_val = getattr(symbolic_double, outer_var).x
generator_val = getattr(generator_double, outer_var).x
for inner_var in coords.variables:
- symbolic_val = symbolic_val.subs(inner_var, k(getattr(self.secp128r1.generator, inner_var).x))
+ symbolic_val = symbolic_val.subs(
+ inner_var, k(getattr(self.secp128r1.generator, inner_var).x)
+ )
self.assertEqual(Mod(int(symbolic_val), p), Mod(generator_val, p))
diff --git a/test/ec/test_key_agreement.py b/test/ec/test_key_agreement.py
index cbdb1c8..240c174 100644
--- a/test/ec/test_key_agreement.py
+++ b/test/ec/test_key_agreement.py
@@ -3,33 +3,40 @@ from unittest import TestCase
from parameterized import parameterized
from pyecsca.ec.params import get_params
-from pyecsca.ec.key_agreement import (ECDH_NONE, ECDH_SHA1, ECDH_SHA224, ECDH_SHA256, ECDH_SHA384,
- ECDH_SHA512)
+from pyecsca.ec.key_agreement import (
+ ECDH_NONE,
+ ECDH_SHA1,
+ ECDH_SHA224,
+ ECDH_SHA256,
+ ECDH_SHA384,
+ ECDH_SHA512,
+)
from pyecsca.ec.mod import Mod
from pyecsca.ec.mult import LTRMultiplier
class KeyAgreementTests(TestCase):
-
def setUp(self):
self.secp128r1 = get_params("secg", "secp128r1", "projective")
self.add = self.secp128r1.curve.coordinate_model.formulas["add-2007-bl"]
self.dbl = self.secp128r1.curve.coordinate_model.formulas["dbl-2007-bl"]
self.mult = LTRMultiplier(self.add, self.dbl)
- self.priv_a = Mod(0xdeadbeef, self.secp128r1.order)
+ self.priv_a = Mod(0xDEADBEEF, self.secp128r1.order)
self.mult.init(self.secp128r1, self.secp128r1.generator)
self.pub_a = self.mult.multiply(int(self.priv_a))
- self.priv_b = Mod(0xcafebabe, self.secp128r1.order)
+ self.priv_b = Mod(0xCAFEBABE, self.secp128r1.order)
self.pub_b = self.mult.multiply(int(self.priv_b))
- @parameterized.expand([
- ("NONE", ECDH_NONE),
- ("SHA1", ECDH_SHA1),
- ("SHA224", ECDH_SHA224),
- ("SHA256", ECDH_SHA256),
- ("SHA384", ECDH_SHA384),
- ("SHA512", ECDH_SHA512)
- ])
+ @parameterized.expand(
+ [
+ ("NONE", ECDH_NONE),
+ ("SHA1", ECDH_SHA1),
+ ("SHA224", ECDH_SHA224),
+ ("SHA256", ECDH_SHA256),
+ ("SHA384", ECDH_SHA384),
+ ("SHA512", ECDH_SHA512),
+ ]
+ )
def test_all(self, name, algo):
result_ab = algo(self.mult, self.secp128r1, self.pub_a, self.priv_b).perform()
result_ba = algo(self.mult, self.secp128r1, self.pub_b, self.priv_a).perform()
diff --git a/test/ec/test_key_generation.py b/test/ec/test_key_generation.py
index f0d926c..7eb26f0 100644
--- a/test/ec/test_key_generation.py
+++ b/test/ec/test_key_generation.py
@@ -6,7 +6,6 @@ from pyecsca.ec.mult import LTRMultiplier
class KeyGenerationTests(TestCase):
-
def setUp(self):
self.secp128r1 = get_params("secg", "secp128r1", "projective")
self.add = self.secp128r1.curve.coordinate_model.formulas["add-2007-bl"]
diff --git a/test/ec/test_mod.py b/test/ec/test_mod.py
index a21ef06..2c45447 100644
--- a/test/ec/test_mod.py
+++ b/test/ec/test_mod.py
@@ -1,13 +1,27 @@
from sympy import FF, symbols
from unittest import TestCase
-from pyecsca.ec.mod import Mod, gcd, extgcd, Undefined, miller_rabin, has_gmp, RawMod, SymbolicMod, jacobi
-from pyecsca.ec.error import NonInvertibleError, NonResidueError, NonInvertibleWarning, NonResidueWarning
+from pyecsca.ec.mod import (
+ Mod,
+ gcd,
+ extgcd,
+ Undefined,
+ miller_rabin,
+ has_gmp,
+ RawMod,
+ SymbolicMod,
+ jacobi,
+)
+from pyecsca.ec.error import (
+ NonInvertibleError,
+ NonResidueError,
+ NonInvertibleWarning,
+ NonResidueWarning,
+)
from pyecsca.misc.cfg import getconfig, TemporaryConfig
class ModTests(TestCase):
-
def test_gcd(self):
self.assertEqual(gcd(15, 20), 5)
self.assertEqual(extgcd(15, 0), (1, 0, 15))
@@ -15,20 +29,33 @@ class ModTests(TestCase):
def test_jacobi(self):
self.assertEqual(jacobi(5, 1153486465415345646578465454655646543248656451), 1)
- self.assertEqual(jacobi(564786456646845, 46874698564153465453246546545456849797895547657), -1)
- self.assertEqual(jacobi(564786456646845, 46874698564153465453246546545456849797895), 0)
+ self.assertEqual(
+ jacobi(564786456646845, 46874698564153465453246546545456849797895547657), -1
+ )
+ self.assertEqual(
+ jacobi(564786456646845, 46874698564153465453246546545456849797895), 0
+ )
def test_miller_rabin(self):
self.assertTrue(miller_rabin(2))
self.assertTrue(miller_rabin(3))
self.assertTrue(miller_rabin(5))
self.assertFalse(miller_rabin(8))
- self.assertTrue(miller_rabin(0xe807561107ccf8fa82af74fd492543a918ca2e9c13750233a9))
- self.assertFalse(miller_rabin(0x6f6889deb08da211927370810f026eb4c17b17755f72ea005))
+ self.assertTrue(
+ miller_rabin(0xE807561107CCF8FA82AF74FD492543A918CA2E9C13750233A9)
+ )
+ self.assertFalse(
+ miller_rabin(0x6F6889DEB08DA211927370810F026EB4C17B17755F72EA005)
+ )
def test_inverse(self):
- p = 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff
- self.assertEqual(Mod(0x702bdafd3c1c837b23a1cb196ed7f9fadb333c5cfe4a462be32adcd67bfb6ac1, p).inverse(), Mod(0x1cb2e5274bba085c4ca88eede75ae77949e7a410c80368376e97ab22eb590f9d, p))
+ p = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF
+ self.assertEqual(
+ Mod(
+ 0x702BDAFD3C1C837B23A1CB196ED7F9FADB333C5CFE4A462BE32ADCD67BFB6AC1, p
+ ).inverse(),
+ Mod(0x1CB2E5274BBA085C4CA88EEDE75AE77949E7A410C80368376E97AB22EB590F9D, p),
+ )
with self.assertRaises(NonInvertibleError):
Mod(0, p).inverse()
with self.assertRaises(NonInvertibleError):
@@ -50,22 +77,42 @@ class ModTests(TestCase):
self.assertTrue(Mod(1, 2).is_residue())
def test_sqrt(self):
- p = 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff
- self.assertIn(Mod(0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc, p).sqrt(), (0x9add512515b70d9ec471151c1dec46625cd18b37bde7ca7fb2c8b31d7033599d, 0x6522aed9ea48f2623b8eeae3e213b99da32e74c9421835804d374ce28fcca662))
+ p = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF
+ self.assertIn(
+ Mod(
+ 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC, p
+ ).sqrt(),
+ (
+ 0x9ADD512515B70D9EC471151C1DEC46625CD18B37BDE7CA7FB2C8B31D7033599D,
+ 0x6522AED9EA48F2623B8EEAE3E213B99DA32E74C9421835804D374CE28FCCA662,
+ ),
+ )
with self.assertRaises(NonResidueError):
- Mod(0x702bdafd3c1c837b23a1cb196ed7f9fadb333c5cfe4a462be32adcd67bfb6ac1, p).sqrt()
+ Mod(
+ 0x702BDAFD3C1C837B23A1CB196ED7F9FADB333C5CFE4A462BE32ADCD67BFB6AC1, p
+ ).sqrt()
getconfig().ec.non_residue_action = "warning"
with self.assertRaises(NonResidueWarning):
- Mod(0x702bdafd3c1c837b23a1cb196ed7f9fadb333c5cfe4a462be32adcd67bfb6ac1, p).sqrt()
+ Mod(
+ 0x702BDAFD3C1C837B23A1CB196ED7F9FADB333C5CFE4A462BE32ADCD67BFB6AC1, p
+ ).sqrt()
getconfig().ec.non_residue_action = "ignore"
- Mod(0x702bdafd3c1c837b23a1cb196ed7f9fadb333c5cfe4a462be32adcd67bfb6ac1, p).sqrt()
+ Mod(
+ 0x702BDAFD3C1C837B23A1CB196ED7F9FADB333C5CFE4A462BE32ADCD67BFB6AC1, p
+ ).sqrt()
with TemporaryConfig() as cfg:
cfg.ec.non_residue_action = "warning"
with self.assertRaises(NonResidueWarning):
- Mod(0x702bdafd3c1c837b23a1cb196ed7f9fadb333c5cfe4a462be32adcd67bfb6ac1, p).sqrt()
+ Mod(
+ 0x702BDAFD3C1C837B23A1CB196ED7F9FADB333C5CFE4A462BE32ADCD67BFB6AC1,
+ p,
+ ).sqrt()
self.assertEqual(Mod(0, p).sqrt(), Mod(0, p))
- q = 0x75d44fee9a71841ae8403c0c251fbad
- self.assertIn(Mod(0x591e0db18cf1bd81a11b2985a821eb3, q).sqrt(), (0x113b41a1a2b73f636e73be3f9a3716e, 0x64990e4cf7ba44b779cc7dcc8ae8a3f))
+ q = 0x75D44FEE9A71841AE8403C0C251FBAD
+ self.assertIn(
+ Mod(0x591E0DB18CF1BD81A11B2985A821EB3, q).sqrt(),
+ (0x113B41A1A2B73F636E73BE3F9A3716E, 0x64990E4CF7BA44B779CC7DCC8AE8A3F),
+ )
getconfig().ec.non_residue_action = "error"
def test_eq(self):
@@ -77,9 +124,9 @@ class ModTests(TestCase):
def test_pow(self):
a = Mod(5, 7)
- self.assertEqual(a**(-1), a.inverse())
- self.assertEqual(a**0, Mod(1, 7))
- self.assertEqual(a**(-2), a.inverse()**2)
+ self.assertEqual(a ** (-1), a.inverse())
+ self.assertEqual(a ** 0, Mod(1, 7))
+ self.assertEqual(a ** (-2), a.inverse() ** 2)
def test_wrong_mod(self):
a = Mod(5, 7)
@@ -91,7 +138,7 @@ class ModTests(TestCase):
a = Mod(5, 7)
c = Mod(4, 11)
with self.assertRaises(TypeError):
- a**c
+ a ** c
def test_other(self):
a = Mod(5, 7)
@@ -116,7 +163,15 @@ class ModTests(TestCase):
def test_undefined(self):
u = Undefined()
for k, meth in u.__class__.__dict__.items():
- if k in ("__module__", "__new__", "__init__", "__doc__", "__hash__", "__abstractmethods__", "_abc_impl"):
+ if k in (
+ "__module__",
+ "__new__",
+ "__init__",
+ "__doc__",
+ "__hash__",
+ "__abstractmethods__",
+ "_abc_impl",
+ ):
continue
args = [5 for _ in range(meth.__code__.co_argcount - 1)]
if k == "__repr__":
diff --git a/test/ec/test_model.py b/test/ec/test_model.py
index b9d4383..d1c03c3 100644
--- a/test/ec/test_model.py
+++ b/test/ec/test_model.py
@@ -1,11 +1,14 @@
from unittest import TestCase
-from pyecsca.ec.model import (ShortWeierstrassModel, MontgomeryModel, EdwardsModel,
- TwistedEdwardsModel)
+from pyecsca.ec.model import (
+ ShortWeierstrassModel,
+ MontgomeryModel,
+ EdwardsModel,
+ TwistedEdwardsModel,
+)
class CurveModelTests(TestCase):
-
def test_load(self):
self.assertGreater(len(ShortWeierstrassModel().coordinates), 0)
self.assertGreater(len(MontgomeryModel().coordinates), 0)
diff --git a/test/ec/test_mult.py b/test/ec/test_mult.py
index 19db2b2..5200520 100644
--- a/test/ec/test_mult.py
+++ b/test/ec/test_mult.py
@@ -3,16 +3,21 @@ from unittest import TestCase
from parameterized import parameterized
from pyecsca.ec.params import get_params
-from pyecsca.ec.mult import (LTRMultiplier, RTLMultiplier, LadderMultiplier, BinaryNAFMultiplier,
- WindowNAFMultiplier, SimpleLadderMultiplier,
- DifferentialLadderMultiplier,
- CoronMultiplier)
+from pyecsca.ec.mult import (
+ LTRMultiplier,
+ RTLMultiplier,
+ LadderMultiplier,
+ BinaryNAFMultiplier,
+ WindowNAFMultiplier,
+ SimpleLadderMultiplier,
+ DifferentialLadderMultiplier,
+ CoronMultiplier,
+)
from pyecsca.ec.point import InfinityPoint
from .utils import cartesian
class ScalarMultiplierTests(TestCase):
-
def setUp(self):
self.secp128r1 = get_params("secg", "secp128r1", "projective")
self.base = self.secp128r1.generator
@@ -31,9 +36,13 @@ class ScalarMultiplierTests(TestCase):
else:
assert one.equals(other)
- def do_basic_test(self, mult_class, params, base, add, dbl, scale, neg=None, **kwargs):
- mult = mult_class(*self.get_formulas(params.curve.coordinate_model, add, dbl, neg, scale),
- **kwargs)
+ def do_basic_test(
+ self, mult_class, params, base, add, dbl, scale, neg=None, **kwargs
+ ):
+ mult = mult_class(
+ *self.get_formulas(params.curve.coordinate_model, add, dbl, neg, scale),
+ **kwargs
+ )
mult.init(params, base)
res = mult.multiply(314)
other = mult.multiply(157)
@@ -44,71 +53,120 @@ class ScalarMultiplierTests(TestCase):
self.assertEqual(InfinityPoint(params.curve.coordinate_model), mult.multiply(0))
return res
- @parameterized.expand([
- ("scaled", "add-1998-cmo", "dbl-1998-cmo", "z"),
- ("complete", "add-2016-rcb", "dbl-2016-rcb", None),
- ("none", "add-1998-cmo", "dbl-1998-cmo", None)
- ])
+ @parameterized.expand(
+ [
+ ("scaled", "add-1998-cmo", "dbl-1998-cmo", "z"),
+ ("complete", "add-2016-rcb", "dbl-2016-rcb", None),
+ ("none", "add-1998-cmo", "dbl-1998-cmo", None),
+ ]
+ )
def test_rtl(self, name, add, dbl, scale):
self.do_basic_test(RTLMultiplier, self.secp128r1, self.base, add, dbl, scale)
- @parameterized.expand([
- ("scaled", "add-1998-cmo", "dbl-1998-cmo", "z"),
- ("complete", "add-2016-rcb", "dbl-2016-rcb", None),
- ("none", "add-1998-cmo", "dbl-1998-cmo", None)
- ])
+ @parameterized.expand(
+ [
+ ("scaled", "add-1998-cmo", "dbl-1998-cmo", "z"),
+ ("complete", "add-2016-rcb", "dbl-2016-rcb", None),
+ ("none", "add-1998-cmo", "dbl-1998-cmo", None),
+ ]
+ )
def test_ltr(self, name, add, dbl, scale):
- a = self.do_basic_test(LTRMultiplier, self.secp128r1, self.base, add, dbl, scale)
- b = self.do_basic_test(LTRMultiplier, self.secp128r1, self.base, add, dbl, scale,
- always=True)
- c = self.do_basic_test(LTRMultiplier, self.secp128r1, self.base, add, dbl, scale,
- complete=False)
- d = self.do_basic_test(LTRMultiplier, self.secp128r1, self.base, add, dbl, scale,
- always=True,
- complete=False)
+ a = self.do_basic_test(
+ LTRMultiplier, self.secp128r1, self.base, add, dbl, scale
+ )
+ b = self.do_basic_test(
+ LTRMultiplier, self.secp128r1, self.base, add, dbl, scale, always=True
+ )
+ c = self.do_basic_test(
+ LTRMultiplier, self.secp128r1, self.base, add, dbl, scale, complete=False
+ )
+ d = self.do_basic_test(
+ LTRMultiplier,
+ self.secp128r1,
+ self.base,
+ add,
+ dbl,
+ scale,
+ always=True,
+ complete=False,
+ )
self.assertPointEquality(a, b, scale)
self.assertPointEquality(b, c, scale)
self.assertPointEquality(c, d, scale)
- @parameterized.expand([
- ("scaled", "add-1998-cmo", "dbl-1998-cmo", "z"),
- ("complete", "add-2016-rcb", "dbl-2016-rcb", None),
- ("none", "add-1998-cmo", "dbl-1998-cmo", None)
- ])
+ @parameterized.expand(
+ [
+ ("scaled", "add-1998-cmo", "dbl-1998-cmo", "z"),
+ ("complete", "add-2016-rcb", "dbl-2016-rcb", None),
+ ("none", "add-1998-cmo", "dbl-1998-cmo", None),
+ ]
+ )
def test_coron(self, name, add, dbl, scale):
self.do_basic_test(CoronMultiplier, self.secp128r1, self.base, add, dbl, scale)
def test_ladder(self):
- a = self.do_basic_test(LadderMultiplier, self.curve25519, self.base25519, "ladd-1987-m",
- "dbl-1987-m", "scale")
- b = self.do_basic_test(LadderMultiplier, self.curve25519, self.base25519, "ladd-1987-m",
- "dbl-1987-m", "scale", complete=False)
+ a = self.do_basic_test(
+ LadderMultiplier,
+ self.curve25519,
+ self.base25519,
+ "ladd-1987-m",
+ "dbl-1987-m",
+ "scale",
+ )
+ b = self.do_basic_test(
+ LadderMultiplier,
+ self.curve25519,
+ self.base25519,
+ "ladd-1987-m",
+ "dbl-1987-m",
+ "scale",
+ complete=False,
+ )
self.assertPointEquality(a, b, True)
- @parameterized.expand([
- ("scaled", "add-1998-cmo", "dbl-1998-cmo", "z"),
- ("complete", "add-2016-rcb", "dbl-2016-rcb", None),
- ("none", "add-1998-cmo", "dbl-1998-cmo", None)
- ])
+ @parameterized.expand(
+ [
+ ("scaled", "add-1998-cmo", "dbl-1998-cmo", "z"),
+ ("complete", "add-2016-rcb", "dbl-2016-rcb", None),
+ ("none", "add-1998-cmo", "dbl-1998-cmo", None),
+ ]
+ )
def test_simple_ladder(self, name, add, dbl, scale):
- self.do_basic_test(SimpleLadderMultiplier, self.secp128r1, self.base, add, dbl, scale)
+ self.do_basic_test(
+ SimpleLadderMultiplier, self.secp128r1, self.base, add, dbl, scale
+ )
- @parameterized.expand([
- ("15", 15, True),
- ("15", 15, False),
- ("2355498743", 2355498743, True),
- ("2355498743", 2355498743, False),
- ("325385790209017329644351321912443757746", 325385790209017329644351321912443757746, True),
- ("325385790209017329644351321912443757746", 325385790209017329644351321912443757746, False)
- ])
+ @parameterized.expand(
+ [
+ ("15", 15, True),
+ ("15", 15, False),
+ ("2355498743", 2355498743, True),
+ ("2355498743", 2355498743, False),
+ (
+ "325385790209017329644351321912443757746",
+ 325385790209017329644351321912443757746,
+ True,
+ ),
+ (
+ "325385790209017329644351321912443757746",
+ 325385790209017329644351321912443757746,
+ False,
+ ),
+ ]
+ )
def test_ladder_differential(self, name, num, complete):
- ladder = LadderMultiplier(self.coords25519.formulas["ladd-1987-m"],
- self.coords25519.formulas["dbl-1987-m"],
- self.coords25519.formulas["scale"], complete=complete)
- differential = DifferentialLadderMultiplier(self.coords25519.formulas["dadd-1987-m"],
- self.coords25519.formulas["dbl-1987-m"],
- self.coords25519.formulas["scale"],
- complete=complete)
+ ladder = LadderMultiplier(
+ self.coords25519.formulas["ladd-1987-m"],
+ self.coords25519.formulas["dbl-1987-m"],
+ self.coords25519.formulas["scale"],
+ complete=complete,
+ )
+ differential = DifferentialLadderMultiplier(
+ self.coords25519.formulas["dadd-1987-m"],
+ self.coords25519.formulas["dbl-1987-m"],
+ self.coords25519.formulas["scale"],
+ complete=complete,
+ )
ladder.init(self.curve25519, self.base25519)
res_ladder = ladder.multiply(num)
differential.init(self.curve25519, self.base25519)
@@ -116,22 +174,28 @@ class ScalarMultiplierTests(TestCase):
self.assertEqual(res_ladder, res_differential)
self.assertEqual(InfinityPoint(self.coords25519), differential.multiply(0))
- @parameterized.expand([
- ("scaled", "add-1998-cmo", "dbl-1998-cmo", "neg", "z"),
- ("complete", "add-2016-rcb", "dbl-2016-rcb", "neg", None),
- ("none", "add-1998-cmo", "dbl-1998-cmo", "neg", None)
- ])
+ @parameterized.expand(
+ [
+ ("scaled", "add-1998-cmo", "dbl-1998-cmo", "neg", "z"),
+ ("complete", "add-2016-rcb", "dbl-2016-rcb", "neg", None),
+ ("none", "add-1998-cmo", "dbl-1998-cmo", "neg", None),
+ ]
+ )
def test_binary_naf(self, name, add, dbl, neg, scale):
- self.do_basic_test(BinaryNAFMultiplier, self.secp128r1, self.base, add, dbl, scale, neg)
+ self.do_basic_test(
+ BinaryNAFMultiplier, self.secp128r1, self.base, add, dbl, scale, neg
+ )
- @parameterized.expand([
- ("scaled3", "add-1998-cmo", "dbl-1998-cmo", "neg", 3, "z"),
- ("none3", "add-1998-cmo", "dbl-1998-cmo", "neg", 3, None),
- ("complete3", "add-2016-rcb", "dbl-2016-rcb", "neg", 3, None),
- ("scaled5", "add-1998-cmo", "dbl-1998-cmo", "neg", 5, "z"),
- ("none5", "add-1998-cmo", "dbl-1998-cmo", "neg", 5, None),
- ("complete5", "add-2016-rcb", "dbl-2016-rcb", "neg", 5, None),
- ])
+ @parameterized.expand(
+ [
+ ("scaled3", "add-1998-cmo", "dbl-1998-cmo", "neg", 3, "z"),
+ ("none3", "add-1998-cmo", "dbl-1998-cmo", "neg", 3, None),
+ ("complete3", "add-2016-rcb", "dbl-2016-rcb", "neg", 3, None),
+ ("scaled5", "add-1998-cmo", "dbl-1998-cmo", "neg", 5, "z"),
+ ("none5", "add-1998-cmo", "dbl-1998-cmo", "neg", 5, None),
+ ("complete5", "add-2016-rcb", "dbl-2016-rcb", "neg", 5, None),
+ ]
+ )
def test_window_naf(self, name, add, dbl, neg, width, scale):
formulas = self.get_formulas(self.coords, add, dbl, neg, scale)
mult = WindowNAFMultiplier(*formulas[:3], width, *formulas[3:])
@@ -144,41 +208,59 @@ class ScalarMultiplierTests(TestCase):
mult.init(self.secp128r1, self.base)
self.assertEqual(InfinityPoint(self.coords), mult.multiply(0))
- mult = WindowNAFMultiplier(*formulas[:3], width, *formulas[3:],
- precompute_negation=True)
+ mult = WindowNAFMultiplier(
+ *formulas[:3], width, *formulas[3:], precompute_negation=True
+ )
mult.init(self.secp128r1, self.base)
res_precompute = mult.multiply(157 * 789)
self.assertPointEquality(res_precompute, res, scale)
- @parameterized.expand(cartesian([
- ("10", 10),
- ("2355498743", 2355498743),
- ("325385790209017329644351321912443757746", 325385790209017329644351321912443757746)
- ], [
- ("add-1998-cmo", "dbl-1998-cmo"),
- ("add-2016-rcb", "dbl-2016-rcb")
- ]))
+ @parameterized.expand(
+ cartesian(
+ [
+ ("10", 10),
+ ("2355498743", 2355498743),
+ (
+ "325385790209017329644351321912443757746",
+ 325385790209017329644351321912443757746,
+ ),
+ ],
+ [("add-1998-cmo", "dbl-1998-cmo"), ("add-2016-rcb", "dbl-2016-rcb")],
+ )
+ )
def test_basic_multipliers(self, name, num, add, dbl):
- ltr = LTRMultiplier(self.coords.formulas[add],
- self.coords.formulas[dbl], self.coords.formulas["z"])
+ ltr = LTRMultiplier(
+ self.coords.formulas[add],
+ self.coords.formulas[dbl],
+ self.coords.formulas["z"],
+ )
with self.assertRaises(ValueError):
ltr.multiply(1)
ltr.init(self.secp128r1, self.base)
res_ltr = ltr.multiply(num)
- rtl = RTLMultiplier(self.coords.formulas[add],
- self.coords.formulas["dbl-1998-cmo"], self.coords.formulas["z"])
+ rtl = RTLMultiplier(
+ self.coords.formulas[add],
+ self.coords.formulas["dbl-1998-cmo"],
+ self.coords.formulas["z"],
+ )
with self.assertRaises(ValueError):
rtl.multiply(1)
rtl.init(self.secp128r1, self.base)
res_rtl = rtl.multiply(num)
self.assertEqual(res_ltr, res_rtl)
- ltr_always = LTRMultiplier(self.coords.formulas[add],
- self.coords.formulas[dbl], self.coords.formulas["z"],
- always=True)
- rtl_always = RTLMultiplier(self.coords.formulas[add],
- self.coords.formulas[dbl], self.coords.formulas["z"],
- always=True)
+ ltr_always = LTRMultiplier(
+ self.coords.formulas[add],
+ self.coords.formulas[dbl],
+ self.coords.formulas["z"],
+ always=True,
+ )
+ rtl_always = RTLMultiplier(
+ self.coords.formulas[add],
+ self.coords.formulas[dbl],
+ self.coords.formulas["z"],
+ always=True,
+ )
ltr_always.init(self.secp128r1, self.base)
rtl_always.init(self.secp128r1, self.base)
res_ltr_always = ltr_always.multiply(num)
@@ -186,36 +268,47 @@ class ScalarMultiplierTests(TestCase):
self.assertEqual(res_ltr, res_ltr_always)
self.assertEqual(res_rtl, res_rtl_always)
- bnaf = BinaryNAFMultiplier(self.coords.formulas[add],
- self.coords.formulas[dbl],
- self.coords.formulas["neg"], self.coords.formulas["z"])
+ bnaf = BinaryNAFMultiplier(
+ self.coords.formulas[add],
+ self.coords.formulas[dbl],
+ self.coords.formulas["neg"],
+ self.coords.formulas["z"],
+ )
with self.assertRaises(ValueError):
bnaf.multiply(1)
bnaf.init(self.secp128r1, self.base)
res_bnaf = bnaf.multiply(num)
self.assertEqual(res_bnaf, res_ltr)
- wnaf = WindowNAFMultiplier(self.coords.formulas[add],
- self.coords.formulas[dbl],
- self.coords.formulas["neg"], 3, self.coords.formulas["z"])
+ wnaf = WindowNAFMultiplier(
+ self.coords.formulas[add],
+ self.coords.formulas[dbl],
+ self.coords.formulas["neg"],
+ 3,
+ self.coords.formulas["z"],
+ )
with self.assertRaises(ValueError):
wnaf.multiply(1)
wnaf.init(self.secp128r1, self.base)
res_wnaf = wnaf.multiply(num)
self.assertEqual(res_wnaf, res_ltr)
- ladder = SimpleLadderMultiplier(self.coords.formulas[add],
- self.coords.formulas[dbl],
- self.coords.formulas["z"])
+ ladder = SimpleLadderMultiplier(
+ self.coords.formulas[add],
+ self.coords.formulas[dbl],
+ self.coords.formulas["z"],
+ )
with self.assertRaises(ValueError):
ladder.multiply(1)
ladder.init(self.secp128r1, self.base)
res_ladder = ladder.multiply(num)
self.assertEqual(res_ladder, res_ltr)
- coron = CoronMultiplier(self.coords.formulas[add],
- self.coords.formulas[dbl],
- self.coords.formulas["z"])
+ coron = CoronMultiplier(
+ self.coords.formulas[add],
+ self.coords.formulas[dbl],
+ self.coords.formulas["z"],
+ )
with self.assertRaises(ValueError):
coron.multiply(1)
coron.init(self.secp128r1, self.base)
@@ -223,12 +316,17 @@ class ScalarMultiplierTests(TestCase):
self.assertEqual(res_coron, res_ltr)
def test_init_fail(self):
- mult = DifferentialLadderMultiplier(self.coords25519.formulas["dadd-1987-m"],
- self.coords25519.formulas["dbl-1987-m"],
- self.coords25519.formulas["scale"])
+ mult = DifferentialLadderMultiplier(
+ self.coords25519.formulas["dadd-1987-m"],
+ self.coords25519.formulas["dbl-1987-m"],
+ self.coords25519.formulas["scale"],
+ )
with self.assertRaises(ValueError):
mult.init(self.secp128r1, self.base)
with self.assertRaises(ValueError):
- LadderMultiplier(self.coords25519.formulas["ladd-1987-m"],
- scl=self.coords25519.formulas["scale"], complete=False)
+ LadderMultiplier(
+ self.coords25519.formulas["ladd-1987-m"],
+ scl=self.coords25519.formulas["scale"],
+ complete=False,
+ )
diff --git a/test/ec/test_naf.py b/test/ec/test_naf.py
index c87c03f..bdc176a 100644
--- a/test/ec/test_naf.py
+++ b/test/ec/test_naf.py
@@ -4,7 +4,6 @@ from pyecsca.ec.naf import naf, wnaf
class NafTests(TestCase):
-
def test_nafs(self):
i = 0b1100110101001101011011
self.assertListEqual(naf(i), wnaf(i, 2))
diff --git a/test/ec/test_op.py b/test/ec/test_op.py
index 019e0e8..c09914f 100644
--- a/test/ec/test_op.py
+++ b/test/ec/test_op.py
@@ -10,28 +10,31 @@ from pyecsca.ec.op import CodeOp, OpType
class OpTests(TestCase):
-
- @parameterized.expand([
- ("add", "x = a+b", "x = a+b", OpType.Add),
- ("sub", "x = a-b", "x = a-b", OpType.Sub),
- ("mul", "y = a*b", "y = a*b", OpType.Mult),
- ("div", "z = a/b", "z = a/b", OpType.Div),
- ("inv", "z = 1/b", "z = 1/b", OpType.Inv),
- ("pow", "b = a**d", "b = a^d", OpType.Pow),
- ("sqr", "b = a**2", "b = a^2", OpType.Sqr),
- ("id1", "b = 7", "b = 7", OpType.Id),
- ("id2", "b = a", "b = a", OpType.Id),
- ])
+ @parameterized.expand(
+ [
+ ("add", "x = a+b", "x = a+b", OpType.Add),
+ ("sub", "x = a-b", "x = a-b", OpType.Sub),
+ ("mul", "y = a*b", "y = a*b", OpType.Mult),
+ ("div", "z = a/b", "z = a/b", OpType.Div),
+ ("inv", "z = 1/b", "z = 1/b", OpType.Inv),
+ ("pow", "b = a**d", "b = a^d", OpType.Pow),
+ ("sqr", "b = a**2", "b = a^2", OpType.Sqr),
+ ("id1", "b = 7", "b = 7", OpType.Id),
+ ("id2", "b = a", "b = a", OpType.Id),
+ ]
+ )
def test_str(self, name, module, result, op_type):
code = parse(module, mode="exec")
op = CodeOp(code)
self.assertEqual(str(op), result)
self.assertEqual(op.operator, op_type)
- @parameterized.expand([
- ("add", "x = a+b", {"a": Mod(5, 21), "b": Mod(7, 21)}, Mod(12, 21)),
- ("sub", "x = a-b", {"a": Mod(7, 21), "b": Mod(5, 21)}, Mod(2, 21))
- ])
+ @parameterized.expand(
+ [
+ ("add", "x = a+b", {"a": Mod(5, 21), "b": Mod(7, 21)}, Mod(12, 21)),
+ ("sub", "x = a-b", {"a": Mod(7, 21), "b": Mod(5, 21)}, Mod(2, 21)),
+ ]
+ )
def test_call(self, name, module, locals, result):
code = parse(module, mode="exec")
op = CodeOp(code)
@@ -40,7 +43,6 @@ class OpTests(TestCase):
class OpResultTests(TestCase):
-
def test_str(self):
for op, char in zip((ast.Add(), ast.Sub(), ast.Mult(), ast.Div()), "+-*/"):
res = OpResult("X1", Mod(0, 5), op, Mod(2, 5), Mod(3, 5))
diff --git a/test/ec/test_params.py b/test/ec/test_params.py
index eb70342..b2a57b6 100644
--- a/test/ec/test_params.py
+++ b/test/ec/test_params.py
@@ -21,15 +21,17 @@ class DomainParameterTests(TestCase):
def test_str(self):
self.assertEqual(str(self.secp128r1), "DomainParameters(secg/secp128r1)")
- @parameterized.expand([
- ("secg/secp128r1", "projective"),
- ("secg/secp256r1", "projective"),
- ("secg/secp521r1", "projective"),
- ("other/Curve25519", "xz"),
- ("other/Ed25519", "projective"),
- ("other/Ed448", "projective"),
- ("other/E-222", "projective")
- ])
+ @parameterized.expand(
+ [
+ ("secg/secp128r1", "projective"),
+ ("secg/secp256r1", "projective"),
+ ("secg/secp521r1", "projective"),
+ ("other/Curve25519", "xz"),
+ ("other/Ed25519", "projective"),
+ ("other/Ed448", "projective"),
+ ("other/E-222", "projective"),
+ ]
+ )
def test_get_params(self, name, coords):
params = get_params(*name.split("/"), coords)
try:
@@ -37,10 +39,15 @@ class DomainParameterTests(TestCase):
except NotImplementedError:
pass
- @parameterized.expand([
- ("anssi", "projective"),
- ("brainpool", lambda name: "projective" if name.endswith("r1") else "jacobian")
- ])
+ @parameterized.expand(
+ [
+ ("anssi", "projective"),
+ (
+ "brainpool",
+ lambda name: "projective" if name.endswith("r1") else "jacobian",
+ ),
+ ]
+ )
def test_get_category(self, name, coords):
get_category(name, coords)
@@ -55,11 +62,13 @@ class DomainParameterTests(TestCase):
category = load_category("test/data/curves.json", "yz")
self.assertEqual(len(category), 1)
- @parameterized.expand([
- ("no_category/some", "else"),
- ("secg/no_curve", "else"),
- ("secg/secp128r1", "some")
- ])
+ @parameterized.expand(
+ [
+ ("no_category/some", "else"),
+ ("secg/no_curve", "else"),
+ ("secg/secp128r1", "some"),
+ ]
+ )
def test_unknown(self, name, coords):
with self.assertRaises(ValueError):
get_params(*name.split("/"), coords)
diff --git a/test/ec/test_point.py b/test/ec/test_point.py
index 9bff800..51907ec 100644
--- a/test/ec/test_point.py
+++ b/test/ec/test_point.py
@@ -19,10 +19,12 @@ class PointTests(TestCase):
Point(self.coords)
def test_to_affine(self):
- pt = Point(self.coords,
- X=Mod(0x161ff7528b899b2d0c28607ca52c5b86, self.secp128r1.curve.prime),
- Y=Mod(0xcf5ac8395bafeb13c02da292dded7a83, self.secp128r1.curve.prime),
- Z=Mod(1, self.secp128r1.curve.prime))
+ pt = Point(
+ self.coords,
+ X=Mod(0x161FF7528B899B2D0C28607CA52C5B86, self.secp128r1.curve.prime),
+ Y=Mod(0xCF5AC8395BAFEB13C02DA292DDED7A83, self.secp128r1.curve.prime),
+ Z=Mod(1, self.secp128r1.curve.prime),
+ )
affine = pt.to_affine()
self.assertIsInstance(affine.coordinate_model, AffineCoordinateModel)
@@ -35,7 +37,11 @@ class PointTests(TestCase):
self.assertIsInstance(affine, InfinityPoint)
def test_to_model(self):
- affine = Point(self.affine, x=Mod(0xabcd, self.secp128r1.curve.prime), y=Mod(0xef, self.secp128r1.curve.prime))
+ affine = Point(
+ self.affine,
+ x=Mod(0xABCD, self.secp128r1.curve.prime),
+ y=Mod(0xEF, self.secp128r1.curve.prime),
+ )
projective_model = self.coords
other = affine.to_model(projective_model, self.secp128r1.curve)
@@ -53,26 +59,34 @@ class PointTests(TestCase):
self.base.to_model(self.coords, self.secp128r1.curve)
def test_to_from_affine(self):
- pt = Point(self.coords,
- X=Mod(0x161ff7528b899b2d0c28607ca52c5b86, self.secp128r1.curve.prime),
- Y=Mod(0xcf5ac8395bafeb13c02da292dded7a83, self.secp128r1.curve.prime),
- Z=Mod(1, self.secp128r1.curve.prime))
+ pt = Point(
+ self.coords,
+ X=Mod(0x161FF7528B899B2D0C28607CA52C5B86, self.secp128r1.curve.prime),
+ Y=Mod(0xCF5AC8395BAFEB13C02DA292DDED7A83, self.secp128r1.curve.prime),
+ Z=Mod(1, self.secp128r1.curve.prime),
+ )
other = pt.to_affine().to_model(self.coords, self.secp128r1.curve)
self.assertEqual(pt, other)
def test_equals(self):
- pt = Point(self.coords,
- X=Mod(0x4, self.secp128r1.curve.prime),
- Y=Mod(0x6, self.secp128r1.curve.prime),
- Z=Mod(2, self.secp128r1.curve.prime))
- other = Point(self.coords,
- X=Mod(0x2, self.secp128r1.curve.prime),
- Y=Mod(0x3, self.secp128r1.curve.prime),
- Z=Mod(1, self.secp128r1.curve.prime))
- third = Point(self.coords,
- X=Mod(0x5, self.secp128r1.curve.prime),
- Y=Mod(0x3, self.secp128r1.curve.prime),
- Z=Mod(1, self.secp128r1.curve.prime))
+ pt = Point(
+ self.coords,
+ X=Mod(0x4, self.secp128r1.curve.prime),
+ Y=Mod(0x6, self.secp128r1.curve.prime),
+ Z=Mod(2, self.secp128r1.curve.prime),
+ )
+ other = Point(
+ self.coords,
+ X=Mod(0x2, self.secp128r1.curve.prime),
+ Y=Mod(0x3, self.secp128r1.curve.prime),
+ Z=Mod(1, self.secp128r1.curve.prime),
+ )
+ third = Point(
+ self.coords,
+ X=Mod(0x5, self.secp128r1.curve.prime),
+ Y=Mod(0x3, self.secp128r1.curve.prime),
+ Z=Mod(1, self.secp128r1.curve.prime),
+ )
self.assertTrue(pt.equals(other))
self.assertNotEqual(pt, other)
self.assertFalse(pt.equals(2))
@@ -94,17 +108,26 @@ class PointTests(TestCase):
self.assertFalse(pt.equals_scaled(infty_one))
mont = MontgomeryModel()
- different = Point(mont.coordinates["xz"],
- X=Mod(0x64daccd2656420216545e5f65221eb,
- 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa),
- Z=Mod(1, 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa))
+ different = Point(
+ mont.coordinates["xz"],
+ X=Mod(
+ 0x64DACCD2656420216545E5F65221EB,
+ 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA,
+ ),
+ Z=Mod(1, 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA),
+ )
self.assertFalse(pt.equals(different))
self.assertNotEqual(pt, different)
def test_bytes(self):
- pt = Point(self.coords,
- X=Mod(0x4, self.secp128r1.curve.prime),
- Y=Mod(0x6, self.secp128r1.curve.prime),
- Z=Mod(2, self.secp128r1.curve.prime))
- self.assertEqual(bytes(pt), b"\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02")
+ pt = Point(
+ self.coords,
+ X=Mod(0x4, self.secp128r1.curve.prime),
+ Y=Mod(0x6, self.secp128r1.curve.prime),
+ Z=Mod(2, self.secp128r1.curve.prime),
+ )
+ self.assertEqual(
+ bytes(pt),
+ b"\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02",
+ )
self.assertEqual(bytes(InfinityPoint(self.coords)), b"\x00")
diff --git a/test/ec/test_regress.py b/test/ec/test_regress.py
index ef593fc..e02cc8e 100644
--- a/test/ec/test_regress.py
+++ b/test/ec/test_regress.py
@@ -10,7 +10,6 @@ from pyecsca.ec.point import Point
class RegressionTests(TestCase):
-
def test_issue_7(self):
secp128r1 = get_params("secg", "secp128r1", "projective")
base = secp128r1.generator
@@ -18,7 +17,9 @@ class RegressionTests(TestCase):
add = coords.formulas["add-1998-cmo"]
dbl = coords.formulas["dbl-1998-cmo"]
scl = coords.formulas["z"]
- mult = LTRMultiplier(add, dbl, scl, always=False, complete=False, short_circuit=True)
+ mult = LTRMultiplier(
+ add, dbl, scl, always=False, complete=False, short_circuit=True
+ )
mult.init(secp128r1, base)
pt = mult.multiply(13613624287328732)
self.assertIsInstance(pt.coords["X"], Mod)
@@ -44,7 +45,9 @@ class RegressionTests(TestCase):
coords = model.coordinates["xz"]
p = 19
neutral = Point(coords, X=Mod(1, p), Z=Mod(0, p))
- curve = EllipticCurve(model, coords, p, neutral, {"a": Mod(8, p), "b": Mod(1, p)})
+ curve = EllipticCurve(
+ model, coords, p, neutral, {"a": Mod(8, p), "b": Mod(1, p)}
+ )
base = Point(coords, X=Mod(12, p), Z=Mod(1, p))
formula = coords.formulas["dbl-1987-m-2"]
res = formula(p, base, **curve.parameters)[0]
@@ -60,13 +63,13 @@ class RegressionTests(TestCase):
model = EdwardsModel()
coords = model.coordinates["yz"]
coords_sqr = model.coordinates["yzsquared"]
- p = 0x1d
+ p = 0x1D
c = Mod(1, p)
- d = Mod(0x1c, p)
+ d = Mod(0x1C, p)
r = d.sqrt()
neutral = Point(coords, Y=c * r, Z=Mod(1, p))
curve = EllipticCurve(model, coords, p, neutral, {"c": c, "d": d, "r": r})
neutral_affine = Point(AffineCoordinateModel(model), x=Mod(0, p), y=c)
self.assertEqual(neutral, neutral_affine.to_model(coords, curve))
- neutral_sqr = Point(coords_sqr, Y=c**2 * r, Z=Mod(1, p))
+ neutral_sqr = Point(coords_sqr, Y=c ** 2 * r, Z=Mod(1, p))
self.assertEqual(neutral_sqr, neutral_affine.to_model(coords_sqr, curve))
diff --git a/test/ec/test_signature.py b/test/ec/test_signature.py
index 8d4a439..f6ab302 100644
--- a/test/ec/test_signature.py
+++ b/test/ec/test_signature.py
@@ -5,29 +5,38 @@ from parameterized import parameterized
from pyecsca.ec.params import get_params
from pyecsca.ec.mod import Mod
from pyecsca.ec.mult import LTRMultiplier
-from pyecsca.ec.signature import (Signature, SignatureResult, ECDSA_NONE, ECDSA_SHA1, ECDSA_SHA224,
- ECDSA_SHA256, ECDSA_SHA384, ECDSA_SHA512)
+from pyecsca.ec.signature import (
+ Signature,
+ SignatureResult,
+ ECDSA_NONE,
+ ECDSA_SHA1,
+ ECDSA_SHA224,
+ ECDSA_SHA256,
+ ECDSA_SHA384,
+ ECDSA_SHA512,
+)
class SignatureTests(TestCase):
-
def setUp(self):
self.secp128r1 = get_params("secg", "secp128r1", "projective")
self.add = self.secp128r1.curve.coordinate_model.formulas["add-2007-bl"]
self.dbl = self.secp128r1.curve.coordinate_model.formulas["dbl-2007-bl"]
self.mult = LTRMultiplier(self.add, self.dbl)
- self.msg = 0xcafebabe.to_bytes(4, byteorder="big")
- self.priv = Mod(0xdeadbeef, self.secp128r1.order)
+ self.msg = 0xCAFEBABE .to_bytes(4, byteorder="big")
+ self.priv = Mod(0xDEADBEEF, self.secp128r1.order)
self.mult.init(self.secp128r1, self.secp128r1.generator)
self.pub = self.mult.multiply(self.priv.x)
- @parameterized.expand([
- ("SHA1", ECDSA_SHA1),
- ("SHA224", ECDSA_SHA224),
- ("SHA256", ECDSA_SHA256),
- ("SHA384", ECDSA_SHA384),
- ("SHA512", ECDSA_SHA512)
- ])
+ @parameterized.expand(
+ [
+ ("SHA1", ECDSA_SHA1),
+ ("SHA224", ECDSA_SHA224),
+ ("SHA256", ECDSA_SHA256),
+ ("SHA384", ECDSA_SHA384),
+ ("SHA512", ECDSA_SHA512),
+ ]
+ )
def test_all(self, name, algo):
signer = algo(self.mult, self.secp128r1, privkey=self.priv)
self.assertTrue(signer.can_sign)
@@ -36,13 +45,17 @@ class SignatureTests(TestCase):
self.assertTrue(verifier.can_verify)
self.assertTrue(verifier.verify_data(sig, self.msg))
- none = ECDSA_NONE(self.mult, self.secp128r1, add=self.add, pubkey=self.pub, privkey=self.priv)
+ none = ECDSA_NONE(
+ self.mult, self.secp128r1, add=self.add, pubkey=self.pub, privkey=self.priv
+ )
digest = signer.hash_algo(self.msg).digest()
sig = none.sign_hash(digest)
self.assertTrue(none.verify_hash(sig, digest))
def test_cannot(self):
- ok = ECDSA_NONE(self.mult, self.secp128r1, add=self.add, pubkey=self.pub, privkey=self.priv)
+ ok = ECDSA_NONE(
+ self.mult, self.secp128r1, add=self.add, pubkey=self.pub, privkey=self.priv
+ )
data = b"aaaa"
sig = ok.sign_data(data)
@@ -60,23 +73,25 @@ class SignatureTests(TestCase):
with self.assertRaises(ValueError):
Signature(self.mult, self.secp128r1)
- @parameterized.expand([
- ("SHA1", ECDSA_SHA1),
- ("SHA224", ECDSA_SHA224),
- ("SHA256", ECDSA_SHA256),
- ("SHA384", ECDSA_SHA384),
- ("SHA512", ECDSA_SHA512)
- ])
+ @parameterized.expand(
+ [
+ ("SHA1", ECDSA_SHA1),
+ ("SHA224", ECDSA_SHA224),
+ ("SHA256", ECDSA_SHA256),
+ ("SHA384", ECDSA_SHA384),
+ ("SHA512", ECDSA_SHA512),
+ ]
+ )
def test_fixed_nonce(self, name, algo):
signer = algo(self.mult, self.secp128r1, privkey=self.priv)
- sig_one = signer.sign_data(self.msg, nonce=0xabcdef)
- sig_other = signer.sign_data(self.msg, nonce=0xabcdef)
+ sig_one = signer.sign_data(self.msg, nonce=0xABCDEF)
+ sig_other = signer.sign_data(self.msg, nonce=0xABCDEF)
verifier = algo(self.mult, self.secp128r1, add=self.add, pubkey=self.pub)
self.assertTrue(verifier.verify_data(sig_one, self.msg))
self.assertTrue(verifier.verify_data(sig_other, self.msg))
self.assertEqual(sig_one, sig_other)
def test_der(self):
- sig = SignatureResult(0xaaaaa, 0xbbbbb)
+ sig = SignatureResult(0xAAAAA, 0xBBBBB)
self.assertEqual(sig, SignatureResult.from_DER(sig.to_DER()))
self.assertNotEqual(sig, "abc")
diff --git a/test/ec/test_transformations.py b/test/ec/test_transformations.py
index 951fbb8..b15f868 100644
--- a/test/ec/test_transformations.py
+++ b/test/ec/test_transformations.py
@@ -5,7 +5,6 @@ from pyecsca.ec.transformations import M2SW, M2TE, TE2M, SW2M, SW2TE
class TransformationTests(TestCase):
-
def test_montgomery(self):
curve25519 = get_params("other", "Curve25519", "affine")
sw = M2SW(curve25519)
diff --git a/test/ec/utils.py b/test/ec/utils.py
index 6429ac9..84c568d 100644
--- a/test/ec/utils.py
+++ b/test/ec/utils.py
@@ -46,12 +46,21 @@ class Profiler(object):
raise ValueError
if self._output_directory is None or self._benchmark_name is None:
return
- git_commit = run(["git", "rev-parse", "--short", "HEAD"], stdout=PIPE, stderr=DEVNULL).stdout.strip().decode()
- git_dirty = run(["git", "diff", "--quiet"], stdout=DEVNULL, stderr=DEVNULL).returncode != 0
+ git_commit = (
+ run(["git", "rev-parse", "--short", "HEAD"], stdout=PIPE, stderr=DEVNULL)
+ .stdout.strip()
+ .decode()
+ )
+ git_dirty = (
+ run(["git", "diff", "--quiet"], stdout=DEVNULL, stderr=DEVNULL).returncode
+ != 0
+ )
version = git_commit + ("-dirty" if git_dirty else "")
output_path = Path(self._output_directory) / (self._benchmark_name + ".csv")
with output_path.open("a") as f:
- f.write(f"{version},{'.'.join(map(str, sys.version_info[:3]))},{self.get_time()}\n")
+ f.write(
+ f"{version},{'.'.join(map(str, sys.version_info[:3]))},{self.get_time()}\n"
+ )
def output(self):
if self._state != "out":
diff --git a/test/sca/test_align.py b/test/sca/test_align.py
index 2630b24..607b297 100644
--- a/test/sca/test_align.py
+++ b/test/sca/test_align.py
@@ -1,28 +1,49 @@
import numpy as np
-from pyecsca.sca import align_correlation, align_peaks, align_sad, align_dtw_scale,\
- align_dtw, Trace, InspectorTraceSet
+from pyecsca.sca import (
+ align_correlation,
+ align_peaks,
+ align_sad,
+ align_dtw_scale,
+ align_dtw,
+ Trace,
+ InspectorTraceSet,
+)
from .utils import Plottable, slow
class AlignTests(Plottable):
-
def test_align(self):
- first_arr = np.array([10, 64, 120, 64, 10, 10, 10, 10, 10], dtype=np.dtype("i1"))
+ first_arr = np.array(
+ [10, 64, 120, 64, 10, 10, 10, 10, 10], dtype=np.dtype("i1")
+ )
second_arr = np.array([10, 10, 10, 10, 50, 80, 50, 20], dtype=np.dtype("i1"))
third_arr = np.array([70, 30, 42, 35, 28, 21, 15, 10, 5], dtype=np.dtype("i1"))
a = Trace(first_arr)
b = Trace(second_arr)
c = Trace(third_arr)
- result, offsets = align_correlation(a, b, c, reference_offset=1, reference_length=3, max_offset=4, min_correlation=0.65)
+ result, offsets = align_correlation(
+ a,
+ b,
+ c,
+ reference_offset=1,
+ reference_length=3,
+ max_offset=4,
+ min_correlation=0.65,
+ )
self.assertIsNotNone(result)
self.assertEqual(len(result), 2)
np.testing.assert_equal(result[0].samples, first_arr)
- np.testing.assert_equal(result[1].samples, np.array([10, 50, 80, 50, 20, 0, 0, 0], dtype=np.dtype("i1")))
+ np.testing.assert_equal(
+ result[1].samples,
+ np.array([10, 50, 80, 50, 20, 0, 0, 0], dtype=np.dtype("i1")),
+ )
@slow
def test_large_align(self):
example = InspectorTraceSet.read("test/data/example.trs")
- result, offsets = align_correlation(*example, reference_offset=100000, reference_length=20000, max_offset=15000)
+ result, offsets = align_correlation(
+ *example, reference_offset=100000, reference_length=20000, max_offset=15000
+ )
self.assertIsNotNone(result)
@slow
@@ -32,25 +53,46 @@ class AlignTests(Plottable):
self.assertIsNotNone(result)
def test_peak_align(self):
- first_arr = np.array([10, 64, 14, 120, 15, 30, 10, 15, 20, 15, 15, 10, 10], dtype=np.dtype("i1"))
- second_arr = np.array([10, 10, 10, 10, 90, 40, 50, 20, 10, 17, 16, 10], dtype=np.dtype("i1"))
+ first_arr = np.array(
+ [10, 64, 14, 120, 15, 30, 10, 15, 20, 15, 15, 10, 10], dtype=np.dtype("i1")
+ )
+ second_arr = np.array(
+ [10, 10, 10, 10, 90, 40, 50, 20, 10, 17, 16, 10], dtype=np.dtype("i1")
+ )
a = Trace(first_arr)
b = Trace(second_arr)
- result, offsets = align_peaks(a, b, reference_offset=2, reference_length=5, max_offset=3)
+ result, offsets = align_peaks(
+ a, b, reference_offset=2, reference_length=5, max_offset=3
+ )
self.assertEqual(np.argmax(result[0].samples), np.argmax(result[1].samples))
def test_sad_align(self):
- first_arr = np.array([10, 64, 14, 120, 15, 30, 10, 15, 20, 15, 15, 10, 10], dtype=np.dtype("i1"))
- second_arr = np.array([10, 10, 90, 40, 50, 20, 10, 17, 16, 10, 10], dtype=np.dtype("i1"))
+ first_arr = np.array(
+ [10, 64, 14, 120, 15, 30, 10, 15, 20, 15, 15, 10, 10], dtype=np.dtype("i1")
+ )
+ second_arr = np.array(
+ [10, 10, 90, 40, 50, 20, 10, 17, 16, 10, 10], dtype=np.dtype("i1")
+ )
a = Trace(first_arr)
b = Trace(second_arr)
- result, offsets = align_sad(a, b, reference_offset=2, reference_length=5, max_offset=3)
+ result, offsets = align_sad(
+ a, b, reference_offset=2, reference_length=5, max_offset=3
+ )
self.assertEqual(len(result), 2)
def test_dtw_align_scale(self):
- first_arr = np.array([10, 64, 14, 120, 15, 30, 10, 15, 20, 15, 15, 10, 10, 8, 10, 12, 10, 13, 9], dtype=np.dtype("f2"))
- second_arr = np.array([10, 10, 60, 40, 90, 20, 10, 17, 16, 10, 10, 10, 10, 10, 17, 12, 10], dtype=np.dtype("f2"))
- third_arr = np.array([10, 30, 20, 21, 15, 8, 10, 37, 21, 77, 20, 28, 25, 10, 9, 10, 15, 9, 10], dtype=np.dtype("f2"))
+ first_arr = np.array(
+ [10, 64, 14, 120, 15, 30, 10, 15, 20, 15, 15, 10, 10, 8, 10, 12, 10, 13, 9],
+ dtype=np.dtype("f2"),
+ )
+ second_arr = np.array(
+ [10, 10, 60, 40, 90, 20, 10, 17, 16, 10, 10, 10, 10, 10, 17, 12, 10],
+ dtype=np.dtype("f2"),
+ )
+ third_arr = np.array(
+ [10, 30, 20, 21, 15, 8, 10, 37, 21, 77, 20, 28, 25, 10, 9, 10, 15, 9, 10],
+ dtype=np.dtype("f2"),
+ )
a = Trace(first_arr)
b = Trace(second_arr)
c = Trace(third_arr)
@@ -62,14 +104,27 @@ class AlignTests(Plottable):
result_other = align_dtw_scale(a, b, c, fast=False)
- self.assertEqual(np.argmax(result_other[0].samples), np.argmax(result_other[1].samples))
- self.assertEqual(np.argmax(result_other[1].samples), np.argmax(result_other[2].samples))
+ self.assertEqual(
+ np.argmax(result_other[0].samples), np.argmax(result_other[1].samples)
+ )
+ self.assertEqual(
+ np.argmax(result_other[1].samples), np.argmax(result_other[2].samples)
+ )
self.plot(*result_other)
def test_dtw_align(self):
- first_arr = np.array([10, 64, 14, 120, 15, 30, 10, 15, 20, 15, 15, 10, 10, 8, 10, 12, 10, 13, 9], dtype=np.dtype("i1"))
- second_arr = np.array([10, 10, 60, 40, 90, 20, 10, 17, 16, 10, 10, 10, 10, 10, 17, 12, 10], dtype=np.dtype("i1"))
- third_arr = np.array([10, 30, 20, 21, 15, 8, 10, 47, 21, 77, 20, 28, 25, 10, 9, 10, 15, 9, 10], dtype=np.dtype("i1"))
+ first_arr = np.array(
+ [10, 64, 14, 120, 15, 30, 10, 15, 20, 15, 15, 10, 10, 8, 10, 12, 10, 13, 9],
+ dtype=np.dtype("i1"),
+ )
+ second_arr = np.array(
+ [10, 10, 60, 40, 90, 20, 10, 17, 16, 10, 10, 10, 10, 10, 17, 12, 10],
+ dtype=np.dtype("i1"),
+ )
+ third_arr = np.array(
+ [10, 30, 20, 21, 15, 8, 10, 47, 21, 77, 20, 28, 25, 10, 9, 10, 15, 9, 10],
+ dtype=np.dtype("i1"),
+ )
a = Trace(first_arr)
b = Trace(second_arr)
c = Trace(third_arr)
@@ -81,6 +136,10 @@ class AlignTests(Plottable):
result_other = align_dtw(a, b, c, fast=False)
- self.assertEqual(np.argmax(result_other[0].samples), np.argmax(result_other[1].samples))
- self.assertEqual(np.argmax(result_other[1].samples), np.argmax(result_other[2].samples))
+ self.assertEqual(
+ np.argmax(result_other[0].samples), np.argmax(result_other[1].samples)
+ )
+ self.assertEqual(
+ np.argmax(result_other[1].samples), np.argmax(result_other[2].samples)
+ )
self.plot(*result_other)
diff --git a/test/sca/test_combine.py b/test/sca/test_combine.py
index 51f7f02..953b4bf 100644
--- a/test/sca/test_combine.py
+++ b/test/sca/test_combine.py
@@ -1,11 +1,20 @@
from unittest import TestCase
import numpy as np
-from pyecsca.sca import Trace, CombinedTrace, average, conditional_average, standard_deviation, variance, average_and_variance, add, subtract
+from pyecsca.sca import (
+ Trace,
+ CombinedTrace,
+ average,
+ conditional_average,
+ standard_deviation,
+ variance,
+ average_and_variance,
+ add,
+ subtract,
+)
class CombineTests(TestCase):
-
def setUp(self):
self.a = Trace(np.array([20, 80], dtype=np.dtype("i1")), {"data": b"\xff"})
self.b = Trace(np.array([30, 42], dtype=np.dtype("i1")), {"data": b"\xff"})
@@ -21,8 +30,12 @@ class CombineTests(TestCase):
self.assertEqual(result.samples[1], 61)
def test_conditional_average(self):
- result = conditional_average(self.a, self.b, self.c,
- condition=lambda trace: trace.meta["data"] == b"\xff")
+ result = conditional_average(
+ self.a,
+ self.b,
+ self.c,
+ condition=lambda trace: trace.meta["data"] == b"\xff",
+ )
self.assertIsInstance(result, CombinedTrace)
self.assertEqual(len(result.samples), 2)
self.assertEqual(result.samples[0], 25)
diff --git a/test/sca/test_edit.py b/test/sca/test_edit.py
index f701d83..282e62e 100644
--- a/test/sca/test_edit.py
+++ b/test/sca/test_edit.py
@@ -6,18 +6,21 @@ from pyecsca.sca import Trace, trim, reverse, pad
class EditTests(TestCase):
-
def setUp(self):
self._trace = Trace(np.array([10, 20, 30, 40, 50], dtype=np.dtype("i1")))
def test_trim(self):
result = trim(self._trace, 2)
self.assertIsNotNone(result)
- np.testing.assert_equal(result.samples, np.array([30, 40, 50], dtype=np.dtype("i1")))
+ np.testing.assert_equal(
+ result.samples, np.array([30, 40, 50], dtype=np.dtype("i1"))
+ )
result = trim(self._trace, end=3)
self.assertIsNotNone(result)
- np.testing.assert_equal(result.samples, np.array([10, 20, 30], dtype=np.dtype("i1")))
+ np.testing.assert_equal(
+ result.samples, np.array([10, 20, 30], dtype=np.dtype("i1"))
+ )
with self.assertRaises(ValueError):
trim(self._trace, 5, 1)
@@ -25,16 +28,21 @@ class EditTests(TestCase):
def test_reverse(self):
result = reverse(self._trace)
self.assertIsNotNone(result)
- np.testing.assert_equal(result.samples,
- np.array([50, 40, 30, 20, 10], dtype=np.dtype("i1")))
+ np.testing.assert_equal(
+ result.samples, np.array([50, 40, 30, 20, 10], dtype=np.dtype("i1"))
+ )
def test_pad(self):
result = pad(self._trace, 2)
self.assertIsNotNone(result)
- np.testing.assert_equal(result.samples,
- np.array([0, 0, 10, 20, 30, 40, 50, 0, 0], dtype=np.dtype("i1")))
+ np.testing.assert_equal(
+ result.samples,
+ np.array([0, 0, 10, 20, 30, 40, 50, 0, 0], dtype=np.dtype("i1")),
+ )
result = pad(self._trace, (1, 3))
self.assertIsNotNone(result)
- np.testing.assert_equal(result.samples,
- np.array([0, 10, 20, 30, 40, 50, 0, 0, 0], dtype=np.dtype("i1")))
+ np.testing.assert_equal(
+ result.samples,
+ np.array([0, 10, 20, 30, 40, 50, 0, 0, 0], dtype=np.dtype("i1")),
+ )
diff --git a/test/sca/test_filter.py b/test/sca/test_filter.py
index 1c9e9ca..9d63ea3 100644
--- a/test/sca/test_filter.py
+++ b/test/sca/test_filter.py
@@ -1,16 +1,25 @@
from unittest import TestCase
import numpy as np
-from pyecsca.sca import Trace, filter_lowpass, filter_highpass, filter_bandpass, filter_bandstop
+from pyecsca.sca import (
+ Trace,
+ filter_lowpass,
+ filter_highpass,
+ filter_bandpass,
+ filter_bandstop,
+)
from .utils import Plottable
class FilterTests(Plottable):
-
def setUp(self):
self._trace = Trace(
- np.array([5, 12, 15, 13, 15, 11, 7, 2, -4, -8, -10, -8, -13, -9, -11, -8, -5],
- dtype=np.dtype("i1")), None)
+ np.array(
+ [5, 12, 15, 13, 15, 11, 7, 2, -4, -8, -10, -8, -13, -9, -11, -8, -5],
+ dtype=np.dtype("i1"),
+ ),
+ None,
+ )
def test_lowpass(self):
result = filter_lowpass(self._trace, 100, 20)
diff --git a/test/sca/test_match.py b/test/sca/test_match.py
index cd0b780..9ba81a3 100644
--- a/test/sca/test_match.py
+++ b/test/sca/test_match.py
@@ -7,22 +7,68 @@ from .utils import Plottable
class MatchingTests(Plottable):
-
def test_simple_match(self):
- pattern = Trace(np.array([1, 15, 12, -10, 0, 13, 17, -1, 0], dtype=np.dtype("i1")), None)
- base = Trace(np.array(
+ pattern = Trace(
+ np.array([1, 15, 12, -10, 0, 13, 17, -1, 0], dtype=np.dtype("i1")), None
+ )
+ base = Trace(
+ np.array(
[0, 1, 3, 1, 2, -2, -3, 1, 15, 12, -10, 0, 13, 17, -1, 0, 3, 1],
- dtype=np.dtype("i1")), None)
+ dtype=np.dtype("i1"),
+ ),
+ None,
+ )
filtered = match_part(base, 7, 9)
self.assertListEqual(filtered, [7])
self.plot(base=base, pattern=pad(pattern, (filtered[0], 0)))
def test_multiple_match(self):
- pattern = Trace(np.array([1, 15, 12, -10, 0, 13, 17, -1, 0], dtype=np.dtype("i1")), None)
- base = Trace(np.array(
- [0, 1, 3, 1, 2, -2, -3, 1, 18, 10, -5, 0, 13, 17, -1, 0, 3, 1, 2, 5, 13, 8, -8, 1,
- 11, 15, 0, 1, 5, 2, 4],
- dtype=np.dtype("i1")), None)
+ pattern = Trace(
+ np.array([1, 15, 12, -10, 0, 13, 17, -1, 0], dtype=np.dtype("i1")), None
+ )
+ base = Trace(
+ np.array(
+ [
+ 0,
+ 1,
+ 3,
+ 1,
+ 2,
+ -2,
+ -3,
+ 1,
+ 18,
+ 10,
+ -5,
+ 0,
+ 13,
+ 17,
+ -1,
+ 0,
+ 3,
+ 1,
+ 2,
+ 5,
+ 13,
+ 8,
+ -8,
+ 1,
+ 11,
+ 15,
+ 0,
+ 1,
+ 5,
+ 2,
+ 4,
+ ],
+ dtype=np.dtype("i1"),
+ ),
+ None,
+ )
filtered = match_pattern(base, pattern, 0.9)
self.assertListEqual(filtered, [7, 19])
- self.plot(base=base, pattern1=pad(pattern, (filtered[0], 0)), pattern2=pad(pattern, (filtered[1], 0)))
+ self.plot(
+ base=base,
+ pattern1=pad(pattern, (filtered[0], 0)),
+ pattern2=pad(pattern, (filtered[1], 0)),
+ )
diff --git a/test/sca/test_plot.py b/test/sca/test_plot.py
index 2cd6b11..7d2cec0 100644
--- a/test/sca/test_plot.py
+++ b/test/sca/test_plot.py
@@ -4,13 +4,17 @@ import numpy as np
import holoviews as hv
import matplotlib as mpl
from pyecsca.sca.trace import Trace
-from pyecsca.sca.trace.plot import (plot_trace, save_figure, save_figure_png, save_figure_svg,
- plot_traces)
+from pyecsca.sca.trace.plot import (
+ plot_trace,
+ save_figure,
+ save_figure_png,
+ save_figure_svg,
+ plot_traces,
+)
from .utils import Plottable
class PlotTests(Plottable):
-
def setUp(self) -> None:
self.trace1 = Trace(np.array([6, 7, 3, -2, 5, 1], dtype=np.dtype("i1")))
self.trace2 = Trace(np.array([2, 3, 7, 0, -1, 0], dtype=np.dtype("i1")))
diff --git a/test/sca/test_process.py b/test/sca/test_process.py
index 4525d01..fda8575 100644
--- a/test/sca/test_process.py
+++ b/test/sca/test_process.py
@@ -1,11 +1,20 @@
from unittest import TestCase
import numpy as np
-from pyecsca.sca import Trace, absolute, invert, threshold, rolling_mean, offset, recenter, normalize, normalize_wl
+from pyecsca.sca import (
+ Trace,
+ absolute,
+ invert,
+ threshold,
+ rolling_mean,
+ offset,
+ recenter,
+ normalize,
+ normalize_wl,
+)
class ProcessTests(TestCase):
-
def setUp(self):
self._trace = Trace(np.array([30, -60, 145, 247], dtype=np.dtype("i2")), None)
@@ -36,7 +45,9 @@ class ProcessTests(TestCase):
def test_offset(self):
result = offset(self._trace, 5)
self.assertIsNotNone(result)
- np.testing.assert_equal(result.samples, np.array([35, -55, 150, 252], dtype=np.dtype("i2")))
+ np.testing.assert_equal(
+ result.samples, np.array([35, -55, 150, 252], dtype=np.dtype("i2"))
+ )
def test_recenter(self):
self.assertIsNotNone(recenter(self._trace))
diff --git a/test/sca/test_rpa.py b/test/sca/test_rpa.py
index 0c96e86..ac8392e 100644
--- a/test/sca/test_rpa.py
+++ b/test/sca/test_rpa.py
@@ -3,14 +3,18 @@ from unittest import TestCase
from parameterized import parameterized
from pyecsca.ec.context import local
-from pyecsca.ec.mult import LTRMultiplier, BinaryNAFMultiplier, WindowNAFMultiplier, LadderMultiplier, \
- DifferentialLadderMultiplier
+from pyecsca.ec.mult import (
+ LTRMultiplier,
+ BinaryNAFMultiplier,
+ WindowNAFMultiplier,
+ LadderMultiplier,
+ DifferentialLadderMultiplier,
+)
from pyecsca.ec.params import get_params
from pyecsca.sca.re.rpa import MultipleContext
class MultipleContextTests(TestCase):
-
def setUp(self):
self.secp128r1 = get_params("secg", "secp128r1", "projective")
self.base = self.secp128r1.generator
@@ -20,14 +24,26 @@ class MultipleContextTests(TestCase):
self.neg = self.coords.formulas["neg"]
self.scale = self.coords.formulas["z"]
- @parameterized.expand([
- ("10", 10),
- ("2355498743", 2355498743),
- ("325385790209017329644351321912443757746", 325385790209017329644351321912443757746),
- ("13613624287328732", 13613624287328732)
- ])
+ @parameterized.expand(
+ [
+ ("10", 10),
+ ("2355498743", 2355498743),
+ (
+ "325385790209017329644351321912443757746",
+ 325385790209017329644351321912443757746,
+ ),
+ ("13613624287328732", 13613624287328732),
+ ]
+ )
def test_basic(self, name, scalar):
- mult = LTRMultiplier(self.add, self.dbl, self.scale, always=False, complete=False, short_circuit=True)
+ mult = LTRMultiplier(
+ self.add,
+ self.dbl,
+ self.scale,
+ always=False,
+ complete=False,
+ short_circuit=True,
+ )
with local(MultipleContext()) as ctx:
mult.init(self.secp128r1, self.base)
mult.multiply(scalar)
diff --git a/test/sca/test_sampling.py b/test/sca/test_sampling.py
index 6108c17..4a54b4d 100644
--- a/test/sca/test_sampling.py
+++ b/test/sca/test_sampling.py
@@ -1,12 +1,18 @@
from unittest import TestCase
import numpy as np
-from pyecsca.sca import Trace, downsample_average, downsample_pick, downsample_decimate, downsample_max, downsample_min
+from pyecsca.sca import (
+ Trace,
+ downsample_average,
+ downsample_pick,
+ downsample_decimate,
+ downsample_max,
+ downsample_min,
+)
from .utils import Plottable
class SamplingTests(Plottable):
-
def setUp(self):
self._trace = Trace(np.array([20, 40, 50, 50, 10], dtype=np.dtype("i1")))
@@ -27,29 +33,136 @@ class SamplingTests(Plottable):
self.assertEqual(result.samples[1], 50)
def test_downsample_max(self):
- trace = Trace(np.array([20, 30, 55, 18, 15, 10, 35, 24, 21, 15, 10, 8, -10, -5,
- -8, -12, -15, -18, -34, -21, -17, -10, -5, -12, -6, -2,
- 4, 8, 21, 28], dtype=np.dtype("i1")))
+ trace = Trace(
+ np.array(
+ [
+ 20,
+ 30,
+ 55,
+ 18,
+ 15,
+ 10,
+ 35,
+ 24,
+ 21,
+ 15,
+ 10,
+ 8,
+ -10,
+ -5,
+ -8,
+ -12,
+ -15,
+ -18,
+ -34,
+ -21,
+ -17,
+ -10,
+ -5,
+ -12,
+ -6,
+ -2,
+ 4,
+ 8,
+ 21,
+ 28,
+ ],
+ dtype=np.dtype("i1"),
+ )
+ )
result = downsample_max(trace, 2)
self.assertIsNotNone(result)
self.assertIsInstance(result, Trace)
self.assertEqual(len(result.samples), 15)
- self.assertEqual(list(result), [30, 55, 15, 35, 21, 10, -5, -8, -15, -21, -10, -5, -2, 8, 28])
+ self.assertEqual(
+ list(result), [30, 55, 15, 35, 21, 10, -5, -8, -15, -21, -10, -5, -2, 8, 28]
+ )
def test_downsample_min(self):
- trace = Trace(np.array([20, 30, 55, 18, 15, 10, 35, 24, 21, 15, 10, 8, -10, -5,
- -8, -12, -15, -18, -34, -21, -17, -10, -5, -12, -6, -2,
- 4, 8, 21, 28], dtype=np.dtype("i1")))
+ trace = Trace(
+ np.array(
+ [
+ 20,
+ 30,
+ 55,
+ 18,
+ 15,
+ 10,
+ 35,
+ 24,
+ 21,
+ 15,
+ 10,
+ 8,
+ -10,
+ -5,
+ -8,
+ -12,
+ -15,
+ -18,
+ -34,
+ -21,
+ -17,
+ -10,
+ -5,
+ -12,
+ -6,
+ -2,
+ 4,
+ 8,
+ 21,
+ 28,
+ ],
+ dtype=np.dtype("i1"),
+ )
+ )
result = downsample_min(trace, 2)
self.assertIsNotNone(result)
self.assertIsInstance(result, Trace)
self.assertEqual(len(result.samples), 15)
- self.assertEqual(list(result), [20, 18, 10, 24, 15, 8, -10, -12, -18, -34, -17, -12, -6, 4, 21])
+ self.assertEqual(
+ list(result),
+ [20, 18, 10, 24, 15, 8, -10, -12, -18, -34, -17, -12, -6, 4, 21],
+ )
def test_downsample_decimate(self):
- trace = Trace(np.array([20, 30, 55, 18, 15, 10, 35, 24, 21, 15, 10, 8, -10, -5,
- -8, -12, -15, -18, -34, -21, -17, -10, -5, -12, -6, -2,
- 4, 8, 21, 28], dtype=np.dtype("i1")))
+ trace = Trace(
+ np.array(
+ [
+ 20,
+ 30,
+ 55,
+ 18,
+ 15,
+ 10,
+ 35,
+ 24,
+ 21,
+ 15,
+ 10,
+ 8,
+ -10,
+ -5,
+ -8,
+ -12,
+ -15,
+ -18,
+ -34,
+ -21,
+ -17,
+ -10,
+ -5,
+ -12,
+ -6,
+ -2,
+ 4,
+ 8,
+ 21,
+ 28,
+ ],
+ dtype=np.dtype("i1"),
+ )
+ )
result = downsample_decimate(trace, 2)
self.assertIsNotNone(result)
self.assertIsInstance(result, Trace)
diff --git a/test/sca/test_target.py b/test/sca/test_target.py
index a0f4fec..4168152 100644
--- a/test/sca/test_target.py
+++ b/test/sca/test_target.py
@@ -12,10 +12,24 @@ from pyecsca.ec.mult import LTRMultiplier
from pyecsca.ec.params import DomainParameters, get_params
from pyecsca.ec.point import Point
from pyecsca.ec.signature import SignatureResult, ECDSA_SHA1
-from pyecsca.sca.target import BinaryTarget, SimpleSerialTarget, SimpleSerialMessage, has_pyscard
-from pyecsca.sca.target.ectester import (KeyAgreementEnum, SignatureEnum, KeypairEnum, KeyBuildEnum,
- KeyClassEnum, CurveEnum, ParameterEnum, RunModeEnum,
- KeyEnum, TransformationEnum)
+from pyecsca.sca.target import (
+ BinaryTarget,
+ SimpleSerialTarget,
+ SimpleSerialMessage,
+ has_pyscard,
+)
+from pyecsca.sca.target.ectester import (
+ KeyAgreementEnum,
+ SignatureEnum,
+ KeypairEnum,
+ KeyBuildEnum,
+ KeyClassEnum,
+ CurveEnum,
+ ParameterEnum,
+ RunModeEnum,
+ KeyEnum,
+ TransformationEnum,
+)
if has_pyscard:
from pyecsca.sca.target.ectester import ECTesterTarget
@@ -28,7 +42,6 @@ class TestTarget(SimpleSerialTarget, BinaryTarget):
class BinaryTargetTests(TestCase):
-
def test_basic_target(self):
target_path = join(dirname(realpath(__file__)), "..", "data", "target.py")
target = TestTarget(["python", target_path])
@@ -67,6 +80,7 @@ class ECTesterTargetTests(TestCase):
if not has_pyscard:
return
from smartcard.System import readers
+
try:
rs = readers()
except BaseSCardException:
@@ -95,35 +109,64 @@ class ECTesterTargetTests(TestCase):
self.assertTrue(ka_resp.success)
sig_resp = self.target.allocate_sig(SignatureEnum.ALG_ECDSA_SHA)
self.assertTrue(sig_resp.success)
- key_resp = self.target.allocate(KeypairEnum.KEYPAIR_LOCAL, KeyBuildEnum.BUILD_KEYPAIR, 256,
- KeyClassEnum.ALG_EC_FP)
+ key_resp = self.target.allocate(
+ KeypairEnum.KEYPAIR_LOCAL,
+ KeyBuildEnum.BUILD_KEYPAIR,
+ 256,
+ KeyClassEnum.ALG_EC_FP,
+ )
self.assertTrue(key_resp.success)
def test_set(self):
- self.target.allocate(KeypairEnum.KEYPAIR_LOCAL, KeyBuildEnum.BUILD_KEYPAIR, 256,
- KeyClassEnum.ALG_EC_FP)
- set_resp = self.target.set(KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1,
- ParameterEnum.DOMAIN_FP)
+ self.target.allocate(
+ KeypairEnum.KEYPAIR_LOCAL,
+ KeyBuildEnum.BUILD_KEYPAIR,
+ 256,
+ KeyClassEnum.ALG_EC_FP,
+ )
+ set_resp = self.target.set(
+ KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP
+ )
self.assertTrue(set_resp.success)
def test_set_explicit(self):
- self.target.allocate(KeypairEnum.KEYPAIR_LOCAL, KeyBuildEnum.BUILD_KEYPAIR, 256,
- KeyClassEnum.ALG_EC_FP)
- values = ECTesterTarget.encode_parameters(ParameterEnum.DOMAIN_FP, self.secp256r1)
- set_resp = self.target.set(KeypairEnum.KEYPAIR_LOCAL, CurveEnum.external,
- ParameterEnum.DOMAIN_FP, values)
+ self.target.allocate(
+ KeypairEnum.KEYPAIR_LOCAL,
+ KeyBuildEnum.BUILD_KEYPAIR,
+ 256,
+ KeyClassEnum.ALG_EC_FP,
+ )
+ values = ECTesterTarget.encode_parameters(
+ ParameterEnum.DOMAIN_FP, self.secp256r1
+ )
+ set_resp = self.target.set(
+ KeypairEnum.KEYPAIR_LOCAL,
+ CurveEnum.external,
+ ParameterEnum.DOMAIN_FP,
+ values,
+ )
self.assertTrue(set_resp.success)
def test_generate(self):
- self.target.allocate(KeypairEnum.KEYPAIR_LOCAL, KeyBuildEnum.BUILD_KEYPAIR, 256,
- KeyClassEnum.ALG_EC_FP)
- self.target.set(KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP)
+ self.target.allocate(
+ KeypairEnum.KEYPAIR_LOCAL,
+ KeyBuildEnum.BUILD_KEYPAIR,
+ 256,
+ KeyClassEnum.ALG_EC_FP,
+ )
+ self.target.set(
+ KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP
+ )
generate_resp = self.target.generate(KeypairEnum.KEYPAIR_LOCAL)
self.assertTrue(generate_resp.success)
def test_clear(self):
- self.target.allocate(KeypairEnum.KEYPAIR_LOCAL, KeyBuildEnum.BUILD_KEYPAIR, 256,
- KeyClassEnum.ALG_EC_FP)
+ self.target.allocate(
+ KeypairEnum.KEYPAIR_LOCAL,
+ KeyBuildEnum.BUILD_KEYPAIR,
+ 256,
+ KeyClassEnum.ALG_EC_FP,
+ )
clear_resp = self.target.clear(KeypairEnum.KEYPAIR_LOCAL)
self.assertTrue(clear_resp.success)
@@ -138,184 +181,312 @@ class ECTesterTargetTests(TestCase):
def test_dry_run(self):
dry_run_resp = self.target.run_mode(RunModeEnum.MODE_DRY_RUN)
self.assertTrue(dry_run_resp.success)
- allocate_resp = self.target.allocate(KeypairEnum.KEYPAIR_LOCAL, KeyBuildEnum.BUILD_KEYPAIR,
- 256,
- KeyClassEnum.ALG_EC_FP)
+ allocate_resp = self.target.allocate(
+ KeypairEnum.KEYPAIR_LOCAL,
+ KeyBuildEnum.BUILD_KEYPAIR,
+ 256,
+ KeyClassEnum.ALG_EC_FP,
+ )
self.assertTrue(allocate_resp.success)
dry_run_resp = self.target.run_mode(RunModeEnum.MODE_NORMAL)
self.assertTrue(dry_run_resp.success)
def test_export(self):
- self.target.allocate(KeypairEnum.KEYPAIR_LOCAL, KeyBuildEnum.BUILD_KEYPAIR, 256,
- KeyClassEnum.ALG_EC_FP)
- self.target.set(KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP)
+ self.target.allocate(
+ KeypairEnum.KEYPAIR_LOCAL,
+ KeyBuildEnum.BUILD_KEYPAIR,
+ 256,
+ KeyClassEnum.ALG_EC_FP,
+ )
+ self.target.set(
+ KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP
+ )
self.target.generate(KeypairEnum.KEYPAIR_LOCAL)
- export_public_resp = self.target.export(KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PUBLIC,
- ParameterEnum.W)
+ export_public_resp = self.target.export(
+ KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PUBLIC, ParameterEnum.W
+ )
self.assertTrue(export_public_resp.success)
- pubkey_bytes = export_public_resp.get_param(KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.W)
+ pubkey_bytes = export_public_resp.get_param(
+ KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.W
+ )
pubkey = self.secp256r1.curve.decode_point(pubkey_bytes)
- export_privkey_resp = self.target.export(KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PRIVATE,
- ParameterEnum.S)
+ export_privkey_resp = self.target.export(
+ KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PRIVATE, ParameterEnum.S
+ )
self.assertTrue(export_privkey_resp.success)
privkey = int.from_bytes(
- export_privkey_resp.get_param(KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.S), "big")
- self.assertEqual(pubkey,
- self.secp256r1.curve.affine_multiply(self.secp256r1.generator, privkey))
+ export_privkey_resp.get_param(KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.S),
+ "big",
+ )
+ self.assertEqual(
+ pubkey,
+ self.secp256r1.curve.affine_multiply(self.secp256r1.generator, privkey),
+ )
def test_export_curve(self):
- self.target.allocate(KeypairEnum.KEYPAIR_LOCAL, KeyBuildEnum.BUILD_KEYPAIR, 256,
- KeyClassEnum.ALG_EC_FP)
- self.target.set(KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP)
- export_resp = self.target.export(KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PUBLIC,
- ParameterEnum.DOMAIN_FP)
+ self.target.allocate(
+ KeypairEnum.KEYPAIR_LOCAL,
+ KeyBuildEnum.BUILD_KEYPAIR,
+ 256,
+ KeyClassEnum.ALG_EC_FP,
+ )
+ self.target.set(
+ KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP
+ )
+ export_resp = self.target.export(
+ KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PUBLIC, ParameterEnum.DOMAIN_FP
+ )
self.assertTrue(export_resp.success)
def test_transform(self):
- self.target.allocate(KeypairEnum.KEYPAIR_LOCAL, KeyBuildEnum.BUILD_KEYPAIR, 256,
- KeyClassEnum.ALG_EC_FP)
- self.target.set(KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP)
+ self.target.allocate(
+ KeypairEnum.KEYPAIR_LOCAL,
+ KeyBuildEnum.BUILD_KEYPAIR,
+ 256,
+ KeyClassEnum.ALG_EC_FP,
+ )
+ self.target.set(
+ KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP
+ )
self.target.generate(KeypairEnum.KEYPAIR_LOCAL)
- export_privkey_resp1 = self.target.export(KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PRIVATE,
- ParameterEnum.S)
+ export_privkey_resp1 = self.target.export(
+ KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PRIVATE, ParameterEnum.S
+ )
privkey = int.from_bytes(
- export_privkey_resp1.get_param(KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.S), "big")
- transform_resp = self.target.transform(KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PRIVATE,
- ParameterEnum.S, TransformationEnum.INCREMENT)
+ export_privkey_resp1.get_param(KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.S),
+ "big",
+ )
+ transform_resp = self.target.transform(
+ KeypairEnum.KEYPAIR_LOCAL,
+ KeyEnum.PRIVATE,
+ ParameterEnum.S,
+ TransformationEnum.INCREMENT,
+ )
self.assertTrue(transform_resp.success)
- export_privkey_resp2 = self.target.export(KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PRIVATE,
- ParameterEnum.S)
+ export_privkey_resp2 = self.target.export(
+ KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PRIVATE, ParameterEnum.S
+ )
privkey_new = int.from_bytes(
- export_privkey_resp2.get_param(KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.S), "big")
+ export_privkey_resp2.get_param(KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.S),
+ "big",
+ )
self.assertEqual(privkey + 1, privkey_new)
def test_ecdh(self):
self.target.allocate_ka(KeyAgreementEnum.ALG_EC_SVDP_DH)
- self.target.allocate(KeypairEnum.KEYPAIR_BOTH, KeyBuildEnum.BUILD_KEYPAIR, 256,
- KeyClassEnum.ALG_EC_FP)
- self.target.set(KeypairEnum.KEYPAIR_BOTH, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP)
+ self.target.allocate(
+ KeypairEnum.KEYPAIR_BOTH,
+ KeyBuildEnum.BUILD_KEYPAIR,
+ 256,
+ KeyClassEnum.ALG_EC_FP,
+ )
+ self.target.set(
+ KeypairEnum.KEYPAIR_BOTH, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP
+ )
self.target.generate(KeypairEnum.KEYPAIR_BOTH)
- ecdh_resp = self.target.ecdh(KeypairEnum.KEYPAIR_LOCAL, KeypairEnum.KEYPAIR_REMOTE, True,
- TransformationEnum.NONE, KeyAgreementEnum.ALG_EC_SVDP_DH)
+ ecdh_resp = self.target.ecdh(
+ KeypairEnum.KEYPAIR_LOCAL,
+ KeypairEnum.KEYPAIR_REMOTE,
+ True,
+ TransformationEnum.NONE,
+ KeyAgreementEnum.ALG_EC_SVDP_DH,
+ )
self.assertTrue(ecdh_resp.success)
- export_public_resp = self.target.export(KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PUBLIC,
- ParameterEnum.W)
- pubkey_bytes = export_public_resp.get_param(KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.W)
+ export_public_resp = self.target.export(
+ KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PUBLIC, ParameterEnum.W
+ )
+ pubkey_bytes = export_public_resp.get_param(
+ KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.W
+ )
pubkey = self.secp256r1.curve.decode_point(pubkey_bytes)
- export_privkey_resp = self.target.export(KeypairEnum.KEYPAIR_REMOTE, KeyEnum.PRIVATE,
- ParameterEnum.S)
- privkey = Mod(int.from_bytes(
- export_privkey_resp.get_param(KeypairEnum.KEYPAIR_REMOTE, ParameterEnum.S), "big"),
- self.secp256r1.curve.prime)
- pubkey_projective = pubkey.to_model(self.secp256r1_projective.curve.coordinate_model, self.secp256r1.curve)
+ export_privkey_resp = self.target.export(
+ KeypairEnum.KEYPAIR_REMOTE, KeyEnum.PRIVATE, ParameterEnum.S
+ )
+ privkey = Mod(
+ int.from_bytes(
+ export_privkey_resp.get_param(
+ KeypairEnum.KEYPAIR_REMOTE, ParameterEnum.S
+ ),
+ "big",
+ ),
+ self.secp256r1.curve.prime,
+ )
+ pubkey_projective = pubkey.to_model(
+ self.secp256r1_projective.curve.coordinate_model, self.secp256r1.curve
+ )
mult = LTRMultiplier(
- self.secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"],
- self.secp256r1_projective.curve.coordinate_model.formulas["dbl-2016-rcb"])
+ self.secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"],
+ self.secp256r1_projective.curve.coordinate_model.formulas["dbl-2016-rcb"],
+ )
ecdh = ECDH_SHA1(mult, self.secp256r1_projective, pubkey_projective, privkey)
expected = ecdh.perform()
self.assertEqual(ecdh_resp.secret, expected)
def test_ecdh_raw(self):
self.target.allocate_ka(KeyAgreementEnum.ALG_EC_SVDP_DH)
- self.target.allocate(KeypairEnum.KEYPAIR_LOCAL, KeyBuildEnum.BUILD_KEYPAIR, 256,
- KeyClassEnum.ALG_EC_FP)
- self.target.set(KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP)
+ self.target.allocate(
+ KeypairEnum.KEYPAIR_LOCAL,
+ KeyBuildEnum.BUILD_KEYPAIR,
+ 256,
+ KeyClassEnum.ALG_EC_FP,
+ )
+ self.target.set(
+ KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP
+ )
self.target.generate(KeypairEnum.KEYPAIR_LOCAL)
mult = LTRMultiplier(
- self.secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"],
- self.secp256r1_projective.curve.coordinate_model.formulas["dbl-2016-rcb"])
+ self.secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"],
+ self.secp256r1_projective.curve.coordinate_model.formulas["dbl-2016-rcb"],
+ )
keygen = KeyGeneration(copy(mult), self.secp256r1_projective)
priv, pubkey_projective = keygen.generate()
- ecdh_resp = self.target.ecdh_direct(KeypairEnum.KEYPAIR_LOCAL, True,
- TransformationEnum.NONE,
- KeyAgreementEnum.ALG_EC_SVDP_DH,
- bytes(pubkey_projective.to_affine()))
+ ecdh_resp = self.target.ecdh_direct(
+ KeypairEnum.KEYPAIR_LOCAL,
+ True,
+ TransformationEnum.NONE,
+ KeyAgreementEnum.ALG_EC_SVDP_DH,
+ bytes(pubkey_projective.to_affine()),
+ )
self.assertTrue(ecdh_resp.success)
- export_privkey_resp = self.target.export(KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PRIVATE,
- ParameterEnum.S)
- privkey = Mod(int.from_bytes(
- export_privkey_resp.get_param(KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.S), "big"),
- self.secp256r1.curve.prime)
+ export_privkey_resp = self.target.export(
+ KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PRIVATE, ParameterEnum.S
+ )
+ privkey = Mod(
+ int.from_bytes(
+ export_privkey_resp.get_param(
+ KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.S
+ ),
+ "big",
+ ),
+ self.secp256r1.curve.prime,
+ )
- ecdh = ECDH_SHA1(copy(mult), self.secp256r1_projective, pubkey_projective, privkey)
+ ecdh = ECDH_SHA1(
+ copy(mult), self.secp256r1_projective, pubkey_projective, privkey
+ )
expected = ecdh.perform()
self.assertEqual(ecdh_resp.secret, expected)
def test_ecdsa(self):
self.target.allocate_sig(SignatureEnum.ALG_ECDSA_SHA)
- self.target.allocate(KeypairEnum.KEYPAIR_LOCAL, KeyBuildEnum.BUILD_KEYPAIR, 256,
- KeyClassEnum.ALG_EC_FP)
- self.target.set(KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP)
+ self.target.allocate(
+ KeypairEnum.KEYPAIR_LOCAL,
+ KeyBuildEnum.BUILD_KEYPAIR,
+ 256,
+ KeyClassEnum.ALG_EC_FP,
+ )
+ self.target.set(
+ KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP
+ )
self.target.generate(KeypairEnum.KEYPAIR_LOCAL)
data = "Some text over here.".encode()
- ecdsa_resp = self.target.ecdsa(KeypairEnum.KEYPAIR_LOCAL, True, SignatureEnum.ALG_ECDSA_SHA,
- data)
+ ecdsa_resp = self.target.ecdsa(
+ KeypairEnum.KEYPAIR_LOCAL, True, SignatureEnum.ALG_ECDSA_SHA, data
+ )
self.assertTrue(ecdsa_resp.success)
- export_public_resp = self.target.export(KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PUBLIC,
- ParameterEnum.W)
- pubkey_bytes = export_public_resp.get_param(KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.W)
+ export_public_resp = self.target.export(
+ KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PUBLIC, ParameterEnum.W
+ )
+ pubkey_bytes = export_public_resp.get_param(
+ KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.W
+ )
pubkey = self.secp256r1.curve.decode_point(pubkey_bytes)
- pubkey_projective = pubkey.to_model(self.secp256r1_projective.curve.coordinate_model, self.secp256r1.curve)
+ pubkey_projective = pubkey.to_model(
+ self.secp256r1_projective.curve.coordinate_model, self.secp256r1.curve
+ )
sig = SignatureResult.from_DER(ecdsa_resp.signature)
mult = LTRMultiplier(
- self.secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"],
- self.secp256r1_projective.curve.coordinate_model.formulas["dbl-2016-rcb"])
- ecdsa = ECDSA_SHA1(copy(mult), self.secp256r1_projective,
- self.secp256r1_projective.curve.coordinate_model.formulas[
- "add-2016-rcb"],
- pubkey_projective)
+ self.secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"],
+ self.secp256r1_projective.curve.coordinate_model.formulas["dbl-2016-rcb"],
+ )
+ ecdsa = ECDSA_SHA1(
+ copy(mult),
+ self.secp256r1_projective,
+ self.secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"],
+ pubkey_projective,
+ )
self.assertTrue(ecdsa.verify_data(sig, data))
def test_ecdsa_sign(self):
self.target.allocate_sig(SignatureEnum.ALG_ECDSA_SHA)
- self.target.allocate(KeypairEnum.KEYPAIR_LOCAL, KeyBuildEnum.BUILD_KEYPAIR, 256,
- KeyClassEnum.ALG_EC_FP)
- self.target.set(KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP)
+ self.target.allocate(
+ KeypairEnum.KEYPAIR_LOCAL,
+ KeyBuildEnum.BUILD_KEYPAIR,
+ 256,
+ KeyClassEnum.ALG_EC_FP,
+ )
+ self.target.set(
+ KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP
+ )
self.target.generate(KeypairEnum.KEYPAIR_LOCAL)
data = "Some text over here.".encode()
- ecdsa_resp = self.target.ecdsa_sign(KeypairEnum.KEYPAIR_LOCAL, True,
- SignatureEnum.ALG_ECDSA_SHA, data)
+ ecdsa_resp = self.target.ecdsa_sign(
+ KeypairEnum.KEYPAIR_LOCAL, True, SignatureEnum.ALG_ECDSA_SHA, data
+ )
self.assertTrue(ecdsa_resp.success)
- export_public_resp = self.target.export(KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PUBLIC,
- ParameterEnum.W)
- pubkey_bytes = export_public_resp.get_param(KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.W)
+ export_public_resp = self.target.export(
+ KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PUBLIC, ParameterEnum.W
+ )
+ pubkey_bytes = export_public_resp.get_param(
+ KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.W
+ )
pubkey = self.secp256r1.curve.decode_point(pubkey_bytes)
- pubkey_projective = pubkey.to_model(self.secp256r1_projective.curve.coordinate_model, self.secp256r1.curve)
+ pubkey_projective = pubkey.to_model(
+ self.secp256r1_projective.curve.coordinate_model, self.secp256r1.curve
+ )
sig = SignatureResult.from_DER(ecdsa_resp.signature)
mult = LTRMultiplier(
- self.secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"],
- self.secp256r1_projective.curve.coordinate_model.formulas["dbl-2016-rcb"])
- ecdsa = ECDSA_SHA1(copy(mult), self.secp256r1_projective,
- self.secp256r1_projective.curve.coordinate_model.formulas[
- "add-2016-rcb"],
- pubkey_projective)
+ self.secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"],
+ self.secp256r1_projective.curve.coordinate_model.formulas["dbl-2016-rcb"],
+ )
+ ecdsa = ECDSA_SHA1(
+ copy(mult),
+ self.secp256r1_projective,
+ self.secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"],
+ pubkey_projective,
+ )
self.assertTrue(ecdsa.verify_data(sig, data))
def test_ecdsa_verify(self):
self.target.allocate_sig(SignatureEnum.ALG_ECDSA_SHA)
- self.target.allocate(KeypairEnum.KEYPAIR_LOCAL, KeyBuildEnum.BUILD_KEYPAIR, 256,
- KeyClassEnum.ALG_EC_FP)
- self.target.set(KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP)
+ self.target.allocate(
+ KeypairEnum.KEYPAIR_LOCAL,
+ KeyBuildEnum.BUILD_KEYPAIR,
+ 256,
+ KeyClassEnum.ALG_EC_FP,
+ )
+ self.target.set(
+ KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP
+ )
mult = LTRMultiplier(
- self.secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"],
- self.secp256r1_projective.curve.coordinate_model.formulas["dbl-2016-rcb"])
+ self.secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"],
+ self.secp256r1_projective.curve.coordinate_model.formulas["dbl-2016-rcb"],
+ )
keygen = KeyGeneration(copy(mult), self.secp256r1_projective)
priv, pubkey_projective = keygen.generate()
- self.target.set(KeypairEnum.KEYPAIR_LOCAL, CurveEnum.external, ParameterEnum.W,
- ECTesterTarget.encode_parameters(ParameterEnum.W,
- pubkey_projective.to_affine()))
- ecdsa = ECDSA_SHA1(copy(mult), self.secp256r1_projective,
- self.secp256r1_projective.curve.coordinate_model.formulas[
- "add-2016-rcb"],
- pubkey_projective,
- priv)
+ self.target.set(
+ KeypairEnum.KEYPAIR_LOCAL,
+ CurveEnum.external,
+ ParameterEnum.W,
+ ECTesterTarget.encode_parameters(
+ ParameterEnum.W, pubkey_projective.to_affine()
+ ),
+ )
+ ecdsa = ECDSA_SHA1(
+ copy(mult),
+ self.secp256r1_projective,
+ self.secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"],
+ pubkey_projective,
+ priv,
+ )
data = "Some text over here.".encode()
sig = ecdsa.sign_data(data)
- ecdsa_resp = self.target.ecdsa_verify(KeypairEnum.KEYPAIR_LOCAL,
- SignatureEnum.ALG_ECDSA_SHA, sig.to_DER(), data)
+ ecdsa_resp = self.target.ecdsa_verify(
+ KeypairEnum.KEYPAIR_LOCAL, SignatureEnum.ALG_ECDSA_SHA, sig.to_DER(), data
+ )
self.assertTrue(ecdsa_resp.success)
diff --git a/test/sca/test_test.py b/test/sca/test_test.py
index 5a20f70..7b4f346 100644
--- a/test/sca/test_test.py
+++ b/test/sca/test_test.py
@@ -6,7 +6,6 @@ from pyecsca.sca import Trace, welch_ttest, student_ttest, ks_test
class TTestTests(TestCase):
-
def setUp(self):
self.a = Trace(np.array([20, 80], dtype=np.dtype("i1")))
self.b = Trace(np.array([30, 42], dtype=np.dtype("i1")))
@@ -15,9 +14,15 @@ class TTestTests(TestCase):
def test_welch_ttest(self):
self.assertIsNotNone(welch_ttest([self.a, self.b], [self.c, self.d]))
- a = Trace(np.array([19.8, 20.4, 19.6, 17.8, 18.5, 18.9, 18.3, 18.9, 19.5, 22.0]))
- b = Trace(np.array([28.2, 26.6, 20.1, 23.3, 25.2, 22.1, 17.7, 27.6, 20.6, 13.7]))
- c = Trace(np.array([20.2, 21.6, 27.1, 13.3, 24.2, 20.1, 11.7, 25.6, 26.6, 21.4]))
+ a = Trace(
+ np.array([19.8, 20.4, 19.6, 17.8, 18.5, 18.9, 18.3, 18.9, 19.5, 22.0])
+ )
+ b = Trace(
+ np.array([28.2, 26.6, 20.1, 23.3, 25.2, 22.1, 17.7, 27.6, 20.6, 13.7])
+ )
+ c = Trace(
+ np.array([20.2, 21.6, 27.1, 13.3, 24.2, 20.1, 11.7, 25.6, 26.6, 21.4])
+ )
result = welch_ttest([a, b], [b, c], dof=True, p_value=True)
self.assertIsNotNone(result)
@@ -28,7 +33,6 @@ class TTestTests(TestCase):
class KolmogorovSmirnovTests(TestCase):
-
def test_ks_test(self):
self.assertIsNone(ks_test([], []))
diff --git a/test/sca/test_trace.py b/test/sca/test_trace.py
index 91b640d..93baa89 100644
--- a/test/sca/test_trace.py
+++ b/test/sca/test_trace.py
@@ -4,7 +4,6 @@ from pyecsca.sca import Trace
class TraceTests(TestCase):
-
def test_basic(self):
trace = Trace(np.array([10, 15, 24], dtype=np.dtype("i1")))
self.assertIsNotNone(trace)
diff --git a/test/sca/test_traceset.py b/test/sca/test_traceset.py
index 7adff41..515790e 100644
--- a/test/sca/test_traceset.py
+++ b/test/sca/test_traceset.py
@@ -6,17 +6,24 @@ from unittest import TestCase
import numpy as np
-from pyecsca.sca import (TraceSet, InspectorTraceSet, ChipWhispererTraceSet, PickleTraceSet,
- HDF5TraceSet, Trace)
+from pyecsca.sca import (
+ TraceSet,
+ InspectorTraceSet,
+ ChipWhispererTraceSet,
+ PickleTraceSet,
+ HDF5TraceSet,
+ Trace,
+)
-EXAMPLE_TRACES = [Trace(np.array([20, 40, 50, 50, 10], dtype=np.dtype("i1")), {"something": 5}),
- Trace(np.array([1, 2, 3, 4, 5], dtype=np.dtype("i1"))),
- Trace(np.array([6, 7, 8, 9, 10], dtype=np.dtype("i1")))]
+EXAMPLE_TRACES = [
+ Trace(np.array([20, 40, 50, 50, 10], dtype=np.dtype("i1")), {"something": 5}),
+ Trace(np.array([1, 2, 3, 4, 5], dtype=np.dtype("i1"))),
+ Trace(np.array([6, 7, 8, 9, 10], dtype=np.dtype("i1"))),
+]
EXAMPLE_KWARGS = {"num_traces": 3, "thingy": "abc"}
class TraceSetTests(TestCase):
-
def test_create(self):
self.assertIsNotNone(TraceSet())
self.assertIsNotNone(InspectorTraceSet())
@@ -26,7 +33,6 @@ class TraceSetTests(TestCase):
class InspectorTraceSetTests(TestCase):
-
def test_load_fname(self):
result = InspectorTraceSet.read("test/data/example.trs")
self.assertIsNotNone(result)
@@ -55,7 +61,6 @@ class InspectorTraceSetTests(TestCase):
class ChipWhispererTraceSetTests(TestCase):
-
def test_load_fname(self):
result = ChipWhispererTraceSet.read("test/data/config_chipwhisperer_.cfg")
self.assertIsNotNone(result)
@@ -63,7 +68,6 @@ class ChipWhispererTraceSetTests(TestCase):
class PickleTraceSetTests(TestCase):
-
def test_load_fname(self):
result = PickleTraceSet.read("test/data/test.pickle")
self.assertIsNotNone(result)
@@ -82,7 +86,6 @@ class PickleTraceSetTests(TestCase):
class HDF5TraceSetTests(TestCase):
-
def test_load_fname(self):
result = HDF5TraceSet.read("test/data/test.h5")
self.assertIsNotNone(result)
@@ -97,8 +100,12 @@ class HDF5TraceSetTests(TestCase):
shutil.copy("test/data/test.h5", path)
trace_set = HDF5TraceSet.inplace(path)
self.assertIsNotNone(trace_set)
- test_trace = Trace(np.array([6, 7], dtype=np.dtype("i1")), meta={"thing": "ring"})
- other_trace = Trace(np.array([15, 7], dtype=np.dtype("i1")), meta={"a": "b"})
+ test_trace = Trace(
+ np.array([6, 7], dtype=np.dtype("i1")), meta={"thing": "ring"}
+ )
+ other_trace = Trace(
+ np.array([15, 7], dtype=np.dtype("i1")), meta={"a": "b"}
+ )
trace_set.append(test_trace)
self.assertEqual(len(trace_set), 4)
trace_set.append(other_trace)
diff --git a/test/sca/utils.py b/test/sca/utils.py
index 0a996cd..b00015b 100644
--- a/test/sca/utils.py
+++ b/test/sca/utils.py
@@ -24,7 +24,6 @@ cases: Dict[str, int] = {}
class Plottable(TestCase):
-
def get_dir(self):
if split(getcwd())[1] == "test":
directory = "plots"