diff options
| -rw-r--r-- | .coveragerc | 1 | ||||
| -rw-r--r-- | .travis.yml | 2 | ||||
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | README.md | 2 | ||||
| -rw-r--r-- | docs/index.rst | 7 | ||||
| -rw-r--r-- | pyecsca/ec/curve.py | 2 | ||||
| -rw-r--r-- | pyecsca/ec/mult.py | 4 | ||||
| -rw-r--r-- | pyecsca/ec/op.py | 8 | ||||
| -rw-r--r-- | pyecsca/ec/params.py | 5 | ||||
| -rw-r--r-- | pyecsca/ec/signature.py | 4 | ||||
| -rw-r--r-- | pyecsca/sca/target/binary.py | 16 | ||||
| -rw-r--r-- | pyecsca/sca/trace/combine.py | 17 | ||||
| -rw-r--r-- | pyecsca/sca/trace_set/base.py | 8 | ||||
| -rw-r--r-- | pyecsca/sca/trace_set/chipwhisperer.py | 8 | ||||
| -rw-r--r-- | pyecsca/sca/trace_set/hdf5.py | 22 | ||||
| -rw-r--r-- | pyecsca/sca/trace_set/inspector.py | 16 | ||||
| -rw-r--r-- | pyecsca/sca/trace_set/pickle.py | 16 | ||||
| -rwxr-xr-x[-rw-r--r--] | test/data/target.py | 1 | ||||
| -rw-r--r-- | test/ec/test_curve.py | 20 | ||||
| -rw-r--r-- | test/ec/test_formula.py | 5 | ||||
| -rw-r--r-- | test/ec/test_mod.py | 11 | ||||
| -rw-r--r-- | test/ec/test_point.py | 4 | ||||
| -rw-r--r-- | test/ec/test_signature.py | 22 | ||||
| -rw-r--r-- | test/sca/test_align.py | 12 | ||||
| -rw-r--r-- | test/sca/test_combine.py | 17 | ||||
| -rw-r--r-- | test/sca/test_plot.py | 2 | ||||
| -rw-r--r-- | test/sca/test_target.py | 16 |
27 files changed, 179 insertions, 71 deletions
diff --git a/.coveragerc b/.coveragerc index 92627e6..75b19fd 100644 --- a/.coveragerc +++ b/.coveragerc @@ -8,6 +8,7 @@ omit = [report] exclude_lines = __repr__ + __str__ pragma: no cover raise AssertionError raise NotImplementedError diff --git a/.travis.yml b/.travis.yml index 9c61f48..5ef16fc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,7 +33,7 @@ install: script: - make -i typecheck - make -i codestyle - - make test + - make test-plots after_success: - codecov @@ -17,7 +17,7 @@ test-all: nose2 -s test -C -v ${TESTS} typecheck: - mypy pyecsca --ignore-missing-imports + mypy pyecsca --ignore-missing-imports --show-error-codes codestyle: flake8 --ignore=E501,F405,F403,F401,E126 pyecsca @@ -1,6 +1,6 @@ #  pyecsca [pɪɛtska] -[](https://travis-ci.com/J08nY/pyecsca) [](https://neuromancer.sk/pyecsca/)  [](https://codecov.io/gh/J08nY/pyecsca) +[](https://neuromancer.sk/pyecsca/) [](https://github.com/J08nY/pyecsca/blob/master/LICENSE) [](https://travis-ci.com/J08nY/pyecsca) [](https://codecov.io/gh/J08nY/pyecsca)  **Py**thon **E**lliptic **C**urve cryptography **S**ide-**C**hannel **A**nalysis toolkit. diff --git a/docs/index.rst b/docs/index.rst index 4067296..af26547 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -4,12 +4,13 @@ pyecsca [pɪɛtska] .. image:: https://img.shields.io/badge/-Github-brightgreen?style=flat&logo=github :target: https://github.com/J08nY/pyecsca +.. image:: https://img.shields.io/github/license/J08nY/pyecsca?color=brightgreen + :target: https://github.com/J08nY/pyecsca/blob/master/LICENSE .. image:: https://img.shields.io/travis/J08nY/pyecsca :target: https://travis-ci.com/J08nY/pyecsca -.. image:: https://img.shields.io/github/license/J08nY/pyecsca.svg - :target: https://github.com/J08nY/pyecsca/blob/master/LICENSE -.. image:: https://codecov.io/gh/J08nY/pyecsca/branch/master/graph/badge.svg +.. 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 **Py**\ thon **E**\ lliptic **C**\ urve cryptography **S**\ ide-**C**\ hannel **A**\ nalysis toolkit. diff --git a/pyecsca/ec/curve.py b/pyecsca/ec/curve.py index a05ff3f..8cbd48e 100644 --- a/pyecsca/ec/curve.py +++ b/pyecsca/ec/curve.py @@ -112,7 +112,7 @@ class EllipticCurve(object): def to_affine(self) -> "EllipticCurve": """Convert this curve into the affine coordinate model, if possible.""" coord_model = AffineCoordinateModel(self.model) - return EllipticCurve(self.model, coord_model, self.prime, self.neutral.to_affine(), self.parameters) + return EllipticCurve(self.model, coord_model, self.prime, self.neutral.to_affine(), self.parameters) # type: ignore[arg-type] def decode_point(self, encoded: bytes) -> Point: """Decode a point encoded as a sequence of bytes (ANSI X9.62).""" diff --git a/pyecsca/ec/mult.py b/pyecsca/ec/mult.py index 6dfd5cd..a3622f5 100644 --- a/pyecsca/ec/mult.py +++ b/pyecsca/ec/mult.py @@ -35,8 +35,8 @@ class ScalarMultiplier(ABC): of the point at infinity. :param formulas: Formulas this instance will use. """ - requires: ClassVar[Set[Type[Formula]]] - optionals: ClassVar[Set[Type[Formula]]] + requires: ClassVar[Set[Type]] #Type[Formula] but mypy has a false positive + optionals: ClassVar[Set[Type]] #Type[Formula] but mypy has a false positive short_circuit: bool formulas: Mapping[str, Formula] _params: DomainParameters diff --git a/pyecsca/ec/op.py b/pyecsca/ec/op.py index ea0c1ad..d0cec58 100644 --- a/pyecsca/ec/op.py +++ b/pyecsca/ec/op.py @@ -1,8 +1,8 @@ from ast import (Module, walk, Name, BinOp, UnaryOp, Constant, Mult, Div, Add, Sub, Pow, Assign, - operator as ast_operator, USub) + operator as ast_operator, unaryop as ast_unaryop, USub) from enum import Enum from types import CodeType -from typing import FrozenSet, cast, Any, Optional +from typing import FrozenSet, cast, Any, Optional, Union from public import public @@ -43,7 +43,7 @@ class CodeOp(object): params = set() variables = set() constants = set() - op = None + op: Optional[Union[ast_operator, ast_unaryop]] = None self.left = None self.right = None for node in walk(assign.value): @@ -80,7 +80,7 @@ class CodeOp(object): else: return None - def __to_op(self, op: Optional[ast_operator], 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 29d431d..8f5afd6 100644 --- a/pyecsca/ec/params.py +++ b/pyecsca/ec/params.py @@ -5,7 +5,7 @@ from typing import Optional, Dict, Union from pkg_resources import resource_listdir, resource_isdir, resource_stream from public import public -from .coordinates import AffineCoordinateModel +from .coordinates import AffineCoordinateModel, CoordinateModel from .curve import EllipticCurve from .mod import Mod from .model import (CurveModel, ShortWeierstrassModel, MontgomeryModel, EdwardsModel, @@ -106,6 +106,7 @@ def get_params(category: str, name: str, coords: str, infty: bool = True) -> Dom params = {name: Mod(int(curve["params"][name], 16), field) for name in param_names} # Check coordinate model name and assumptions + coord_model: CoordinateModel if coords == "affine": coord_model = AffineCoordinateModel(model) else: @@ -138,7 +139,7 @@ def get_params(category: str, name: str, coords: str, infty: bool = True) -> Dom value = Mod(value, field) infinity_coords[coordinate] = value infinity = Point(coord_model, **infinity_coords) - elliptic_curve = EllipticCurve(model, coord_model, field, infinity, params) + elliptic_curve = EllipticCurve(model, coord_model, field, infinity, params) # type: ignore[arg-type] affine = Point(AffineCoordinateModel(model), x=Mod(int(curve["generator"]["x"], 16), field), y=Mod(int(curve["generator"]["y"], 16), field)) if not isinstance(coord_model, AffineCoordinateModel): diff --git a/pyecsca/ec/signature.py b/pyecsca/ec/signature.py index 569367e..e61dc1b 100644 --- a/pyecsca/ec/signature.py +++ b/pyecsca/ec/signature.py @@ -163,7 +163,7 @@ class Signature(object): def sign_data(self, data: bytes, nonce: Optional[int] = None) -> SignatureResult: """Sign data.""" - if not self.can_sign: + if not self.can_sign or self.privkey is None: raise RuntimeError("This instance cannot sign.") with ECDSASignAction(self.params, self.hash_algo, data, self.privkey): k = self._get_nonce(nonce) @@ -199,7 +199,7 @@ class Signature(object): def verify_data(self, signature: SignatureResult, data: bytes) -> bool: """Verify data.""" - if not self.can_verify: + 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): if self.hash_algo is None: diff --git a/pyecsca/sca/target/binary.py b/pyecsca/sca/target/binary.py index cb4c918..f21c2e7 100644 --- a/pyecsca/sca/target/binary.py +++ b/pyecsca/sca/target/binary.py @@ -10,7 +10,7 @@ from .serial import SerialTarget @public class BinaryTarget(SerialTarget): binary: List[str] - process: Optional[Popen] + process: Optional[Popen] = None debug_output: bool def __init__(self, binary: Union[str, List[str]], debug_output: bool = False, **kwargs): @@ -31,16 +31,20 @@ class BinaryTarget(SerialTarget): raise ValueError if self.debug_output: print(">>", data.decode()) - self.process.stdin.write(data.decode()) - self.process.stdin.flush() + if self.process.stdin: + self.process.stdin.write(data.decode()) + self.process.stdin.flush() def read(self, num: int = 0, timeout: int = 0) -> bytes: if self.process is None: raise ValueError - if num != 0: - read = self.process.stdout.readline(num) + if self.process.stdout: + if num != 0: + read = self.process.stdout.readline(num) + else: + read = self.process.stdout.readline() else: - read = self.process.stdout.readline() + read = bytes() # pragma: no cover if self.debug_output: print("<<", read, end="") return read.encode() diff --git a/pyecsca/sca/trace/combine.py b/pyecsca/sca/trace/combine.py index 5eeaaec..26a3e98 100644 --- a/pyecsca/sca/trace/combine.py +++ b/pyecsca/sca/trace/combine.py @@ -52,6 +52,23 @@ def standard_deviation(*traces: Trace) -> Optional[CombinedTrace]: @public +def add(*traces: Trace) -> Optional[CombinedTrace]: + """ + Add `traces`, sample-wise. + + :param traces: + :return: + """ + if not traces: + return None + if len(traces) == 1: + return CombinedTrace(traces[0].samples.copy()) + dtype = traces[0].samples.dtype + result_samples = np.sum(np.array([trace.samples for trace in traces]), axis=0).astype(dtype) + return CombinedTrace(result_samples) + + +@public def subtract(one: Trace, other: Trace) -> CombinedTrace: """ Subtract `other` from `one`, sample-wise. diff --git a/pyecsca/sca/trace_set/base.py b/pyecsca/sca/trace_set/base.py index 1826a98..7cd80b6 100644 --- a/pyecsca/sca/trace_set/base.py +++ b/pyecsca/sca/trace_set/base.py @@ -1,6 +1,6 @@ from io import RawIOBase, BufferedIOBase from pathlib import Path -from typing import List, Union +from typing import List, Union, BinaryIO from public import public @@ -32,14 +32,14 @@ class TraceSet(object): yield from self._traces @classmethod - def read(cls, input: Union[str, Path, bytes, RawIOBase, BufferedIOBase]) -> "TraceSet": + def read(cls, input: Union[str, Path, bytes, BinaryIO]) -> "TraceSet": raise NotImplementedError @classmethod - def inplace(cls, input: Union[str, Path, bytes, RawIOBase, BufferedIOBase]) -> "TraceSet": + def inplace(cls, input: Union[str, Path, bytes, BinaryIO]) -> "TraceSet": raise NotImplementedError - def write(self, output: Union[str, Path, RawIOBase, BufferedIOBase]): + def write(self, output: Union[str, Path, BinaryIO]): raise NotImplementedError def __repr__(self): diff --git a/pyecsca/sca/trace_set/chipwhisperer.py b/pyecsca/sca/trace_set/chipwhisperer.py index ea1fc99..05606ee 100644 --- a/pyecsca/sca/trace_set/chipwhisperer.py +++ b/pyecsca/sca/trace_set/chipwhisperer.py @@ -3,7 +3,7 @@ from io import RawIOBase, BufferedIOBase from itertools import zip_longest from os.path import exists, isfile, join, basename, dirname from pathlib import Path -from typing import Union +from typing import Union, BinaryIO import numpy as np from public import public @@ -18,7 +18,7 @@ class ChipWhispererTraceSet(TraceSet): @classmethod def read(cls, - input: Union[str, Path, bytes, RawIOBase, BufferedIOBase]) -> "ChipWhispererTraceSet": + input: Union[str, Path, bytes, BinaryIO]) -> "ChipWhispererTraceSet": if isinstance(input, (str, Path)): traces, kwargs = ChipWhispererTraceSet.__read(input) return ChipWhispererTraceSet(*traces, **kwargs) @@ -26,10 +26,10 @@ class ChipWhispererTraceSet(TraceSet): raise ValueError @classmethod - def inplace(cls, input: Union[str, Path, bytes, RawIOBase, BufferedIOBase]) -> "ChipWhispererTraceSet": + def inplace(cls, input: Union[str, Path, bytes, BinaryIO]) -> "ChipWhispererTraceSet": raise NotImplementedError - def write(self, output: Union[str, Path, RawIOBase, BufferedIOBase]): + def write(self, output: Union[str, Path, BinaryIO]): raise NotImplementedError @classmethod diff --git a/pyecsca/sca/trace_set/hdf5.py b/pyecsca/sca/trace_set/hdf5.py index b610735..7446bed 100644 --- a/pyecsca/sca/trace_set/hdf5.py +++ b/pyecsca/sca/trace_set/hdf5.py @@ -3,7 +3,7 @@ import uuid from collections import MutableMapping from io import RawIOBase, BufferedIOBase, IOBase from pathlib import Path -from typing import Union, Optional, Dict, Any, List +from typing import Union, Optional, Dict, Any, List, BinaryIO import h5py import numpy as np @@ -49,7 +49,7 @@ class HDF5Meta(MutableMapping): @public class HDF5TraceSet(TraceSet): _file: Optional[h5py.File] - _ordering: Optional[List[str]] + _ordering: List[str] #_meta: Optional[HDF5Meta] def __init__(self, *traces: Trace, _file: Optional[h5py.File] = None, @@ -62,13 +62,13 @@ class HDF5TraceSet(TraceSet): @classmethod - def read(cls, input: Union[str, Path, bytes, RawIOBase, BufferedIOBase]) -> "HDF5TraceSet": + def read(cls, input: Union[str, Path, bytes, BinaryIO]) -> "HDF5TraceSet": if isinstance(input, (str, Path)): hdf5 = h5py.File(str(input), mode="r") - elif isinstance(input, IOBase): + elif isinstance(input, (RawIOBase, BufferedIOBase, BinaryIO)): hdf5 = h5py.File(input, mode="r") else: - raise ValueError + raise TypeError kwargs = dict(hdf5.attrs) kwargs["_ordering"] = list(kwargs["_ordering"]) if "_ordering" in kwargs else list(hdf5.keys()) traces = [] @@ -80,13 +80,13 @@ class HDF5TraceSet(TraceSet): return HDF5TraceSet(*traces, **kwargs) @classmethod - def inplace(cls, input: Union[str, Path, bytes, RawIOBase, BufferedIOBase]) -> "HDF5TraceSet": + def inplace(cls, input: Union[str, Path, bytes, BinaryIO]) -> "HDF5TraceSet": if isinstance(input, (str, Path)): hdf5 = h5py.File(str(input), mode="a") - elif isinstance(input, IOBase): + elif isinstance(input, (RawIOBase, BufferedIOBase, BinaryIO)): hdf5 = h5py.File(input, mode="a") else: - raise ValueError + raise TypeError kwargs = dict(hdf5.attrs) kwargs["_ordering"] = list(kwargs["_ordering"]) if "_ordering" in kwargs else list(hdf5.keys()) traces = [] @@ -94,7 +94,7 @@ class HDF5TraceSet(TraceSet): meta = HDF5Meta(hdf5[k].attrs) samples = hdf5[k] traces.append(Trace(samples, meta)) - return HDF5TraceSet(*traces, **kwargs, _file=hdf5) + return HDF5TraceSet(*traces, **kwargs, _file=hdf5) # type: ignore[misc] def insert(self, index: int, value: Trace) -> Trace: key = str(uuid.uuid4()) @@ -147,10 +147,10 @@ class HDF5TraceSet(TraceSet): # else: # super().__setattr__(key, value) - def write(self, output: Union[str, Path, RawIOBase, BufferedIOBase]): + def write(self, output: Union[str, Path, BinaryIO]): if isinstance(output, (str, Path)): hdf5 = h5py.File(str(output), "w") - elif isinstance(output, IOBase): + elif isinstance(output, BinaryIO): hdf5 = h5py.File(output, "w") else: raise ValueError diff --git a/pyecsca/sca/trace_set/inspector.py b/pyecsca/sca/trace_set/inspector.py index d95e5b7..4fabb9c 100644 --- a/pyecsca/sca/trace_set/inspector.py +++ b/pyecsca/sca/trace_set/inspector.py @@ -2,7 +2,7 @@ import struct from enum import IntEnum from io import BytesIO, RawIOBase, BufferedIOBase, UnsupportedOperation from pathlib import Path -from typing import Union, Optional +from typing import Union, Optional, BinaryIO import numpy as np from public import public @@ -138,7 +138,7 @@ class InspectorTraceSet(TraceSet): } @classmethod - def read(cls, input: Union[str, Path, bytes, RawIOBase, BufferedIOBase]) -> "TraceSet": + def read(cls, input: Union[str, Path, bytes, BinaryIO]) -> "TraceSet": """ Read Inspector trace set from file path, bytes or file-like object. @@ -151,10 +151,10 @@ class InspectorTraceSet(TraceSet): elif isinstance(input, (str, Path)): with open(input, "rb") as f: traces, tags = InspectorTraceSet.__read(f) - elif isinstance(input, (RawIOBase, BufferedIOBase)): + elif isinstance(input, (RawIOBase, BufferedIOBase, BinaryIO)): traces, tags = InspectorTraceSet.__read(input) else: - raise ValueError + raise TypeError for trace in traces: new = InspectorTraceSet.__scale(trace.samples, tags["y_scale"]) del trace.samples @@ -194,10 +194,10 @@ class InspectorTraceSet(TraceSet): return result, tags @classmethod - def inplace(cls, input: Union[str, Path, bytes, RawIOBase, BufferedIOBase]) -> "TraceSet": + def inplace(cls, input: Union[str, Path, bytes, BinaryIO]) -> "TraceSet": raise NotImplementedError - def write(self, output: Union[str, Path, RawIOBase, BufferedIOBase]): + def write(self, output: Union[str, Path, BinaryIO]): """ Save this trace set into a file. @@ -206,10 +206,10 @@ class InspectorTraceSet(TraceSet): if isinstance(output, (str, Path)): with open(output, "wb") as f: self.__write(f) - elif isinstance(output, (RawIOBase, BufferedIOBase)): + elif isinstance(output, (RawIOBase, BufferedIOBase, BinaryIO)): self.__write(output) else: - raise ValueError + raise TypeError def __write(self, file): for tag, tag_tuple in self._tag_parsers.items(): diff --git a/pyecsca/sca/trace_set/pickle.py b/pyecsca/sca/trace_set/pickle.py index 42bc3bb..f754bbf 100644 --- a/pyecsca/sca/trace_set/pickle.py +++ b/pyecsca/sca/trace_set/pickle.py @@ -1,7 +1,7 @@ import pickle from io import BufferedIOBase, RawIOBase, IOBase from pathlib import Path -from typing import Union +from typing import Union, BinaryIO from public import public @@ -11,25 +11,25 @@ from .base import TraceSet @public class PickleTraceSet(TraceSet): @classmethod - def read(cls, input: Union[str, Path, bytes, RawIOBase, BufferedIOBase]) -> "PickleTraceSet": + def read(cls, input: Union[str, Path, bytes, BinaryIO]) -> "PickleTraceSet": if isinstance(input, bytes): return pickle.loads(input) elif isinstance(input, (str, Path)): with open(input, "rb") as f: return pickle.load(f) - elif isinstance(input, IOBase): + elif isinstance(input, (RawIOBase, BufferedIOBase, BinaryIO)): return pickle.load(input) - raise ValueError + raise TypeError @classmethod - def inplace(cls, input: Union[str, Path, bytes, RawIOBase, BufferedIOBase]) -> "PickleTraceSet": + def inplace(cls, input: Union[str, Path, bytes, BinaryIO]) -> "PickleTraceSet": raise NotImplementedError - def write(self, output: Union[str, Path, RawIOBase, BufferedIOBase]): + def write(self, output: Union[str, Path, BinaryIO]): if isinstance(output, (str, Path)): with open(output, "wb") as f: pickle.dump(self, f) - elif isinstance(output, IOBase): + elif isinstance(output, (RawIOBase, BufferedIOBase, BinaryIO)): pickle.dump(self, output) else: - raise ValueError + raise TypeError diff --git a/test/data/target.py b/test/data/target.py index 1583597..9e6e0e0 100644..100755 --- a/test/data/target.py +++ b/test/data/target.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python3 from sys import stdout if __name__ == "__main__": diff --git a/test/ec/test_curve.py b/test/ec/test_curve.py index 1b22279..f9e3387 100644 --- a/test/ec/test_curve.py +++ b/test/ec/test_curve.py @@ -1,7 +1,6 @@ from binascii import unhexlify from unittest import TestCase -from pyecsca.ec.coordinates import AffineCoordinateModel from pyecsca.ec.curve import EllipticCurve from pyecsca.ec.params import get_params from pyecsca.ec.mod import Mod @@ -35,6 +34,7 @@ class CurveTests(TestCase): 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), @@ -46,6 +46,7 @@ class CurveTests(TestCase): 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): self.assertIsNotNone(self.secp128r1.curve.affine_add(self.affine_base, self.affine_base)) @@ -88,4 +89,21 @@ class CurveTests(TestCase): affine_compressed_bytes = unhexlify("03161ff7528b899b2d0c28607ca52c5b86") decoded_compressed = affine_curve.decode_point(affine_compressed_bytes) self.assertEqual(decoded_compressed, affine_point) + affine_compressed_bytes = unhexlify("02161ff7528b899b2d0c28607ca52c5b86") + decoded_compressed = affine_curve.decode_point(affine_compressed_bytes) + decoded_compressed = self.secp128r1.curve.affine_negate(decoded_compressed) + self.assertEqual(decoded_compressed, affine_point) + + infinity_bytes = unhexlify("00") + decoded_infinity = affine_curve.decode_point(infinity_bytes) + self.assertEqual(affine_curve.neutral, decoded_infinity) + + with self.assertRaises(ValueError): + affine_curve.decode_point(unhexlify("03161ff7528b899b2d0c28607ca52c5b")) + with self.assertRaises(ValueError): + affine_curve.decode_point(unhexlify("04161ff7528b899b2d0c28607ca52c5b2c5b2c5b2c5b")) + with self.assertRaises(ValueError): + affine_curve.decode_point(unhexlify("7a161ff7528b899b2d0c28607ca52c5b86")) + with self.assertRaises(ValueError): + affine_curve.decode_point(unhexlify("03161ff7528b899b2d0c28607ca52c5b88")) diff --git a/test/ec/test_formula.py b/test/ec/test_formula.py index c0eed28..d5f8392 100644 --- a/test/ec/test_formula.py +++ b/test/ec/test_formula.py @@ -8,6 +8,7 @@ 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"] def test_wrong_call(self): with self.assertRaises(ValueError): @@ -23,6 +24,10 @@ class FormulaTests(TestCase): self.assertEqual(self.add.inputs, {"X1", "Y1", "Z1", "X2", "Y2", "Z2"}) self.assertEqual(self.add.outputs, {"X3", "Y3", "Z3"}) + def test_eq(self): + self.assertEqual(self.add, self.add) + self.assertNotEqual(self.add, self.dbl) + def test_num_ops(self): self.assertEqual(self.add.num_operations, 33) self.assertEqual(self.add.num_multiplications, 17) diff --git a/test/ec/test_mod.py b/test/ec/test_mod.py index 59c8e24..db54456 100644 --- a/test/ec/test_mod.py +++ b/test/ec/test_mod.py @@ -21,10 +21,21 @@ class ModTests(TestCase): def test_is_residue(self): self.assertTrue(Mod(4, 11).is_residue()) self.assertFalse(Mod(11, 31).is_residue()) + self.assertTrue(Mod(0, 7).is_residue()) + self.assertTrue(Mod(1, 2).is_residue()) def test_sqrt(self): p = 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff self.assertIn(Mod(0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc, p).sqrt(), (0x9add512515b70d9ec471151c1dec46625cd18b37bde7ca7fb2c8b31d7033599d, 0x6522aed9ea48f2623b8eeae3e213b99da32e74c9421835804d374ce28fcca662)) + q = 0x75d44fee9a71841ae8403c0c251fbad + self.assertIn(Mod(0x591e0db18cf1bd81a11b2985a821eb3, q).sqrt(), (0x113b41a1a2b73f636e73be3f9a3716e, 0x64990e4cf7ba44b779cc7dcc8ae8a3f)) + + def test_eq(self): + self.assertEqual(Mod(1, 7), 1) + self.assertNotEqual(Mod(1, 7), "1") + self.assertEqual(Mod(1, 7), Mod(1, 7)) + self.assertNotEqual(Mod(1, 7), Mod(5, 7)) + self.assertNotEqual(Mod(1, 7), Mod(1, 5)) def test_wrong_mod(self): a = Mod(5, 7) diff --git a/test/ec/test_point.py b/test/ec/test_point.py index b0ed051..936d4e9 100644 --- a/test/ec/test_point.py +++ b/test/ec/test_point.py @@ -14,6 +14,10 @@ class PointTests(TestCase): self.coords = self.secp128r1.curve.coordinate_model self.affine = AffineCoordinateModel(ShortWeierstrassModel()) + def test_construction(self): + with self.assertRaises(ValueError): + Point(self.coords) + def test_to_affine(self): pt = Point(self.coords, X=Mod(0x161ff7528b899b2d0c28607ca52c5b86, self.secp128r1.curve.prime), diff --git a/test/ec/test_signature.py b/test/ec/test_signature.py index 0d3c5a9..8d4a439 100644 --- a/test/ec/test_signature.py +++ b/test/ec/test_signature.py @@ -30,17 +30,16 @@ class SignatureTests(TestCase): ]) def test_all(self, name, algo): signer = algo(self.mult, self.secp128r1, privkey=self.priv) - assert signer.can_sign + self.assertTrue(signer.can_sign) sig = signer.sign_data(self.msg) verifier = algo(self.mult, self.secp128r1, add=self.add, pubkey=self.pub) - assert verifier.can_verify - assert verifier.verify_data(sig, self.msg) - # none = ECDSA_NONE(self.mult, add=self.add, pubkey=self.pub, privkey=self.priv) - # digest = sha1(self.msg).digest() - # sig = none.sign_hash(digest) - # assert none.verify_hash(sig, digest) - # sig = none.sign_data(digest) - # assert none.verify_data(sig, digest) + 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) + 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) @@ -73,10 +72,11 @@ class SignatureTests(TestCase): 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) - assert verifier.verify_data(sig_one, self.msg) - assert verifier.verify_data(sig_other, self.msg) + 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) self.assertEqual(sig, SignatureResult.from_DER(sig.to_DER())) + self.assertNotEqual(sig, "abc") diff --git a/test/sca/test_align.py b/test/sca/test_align.py index c36cff0..15d452a 100644 --- a/test/sca/test_align.py +++ b/test/sca/test_align.py @@ -60,6 +60,12 @@ class AlignTests(Plottable): self.assertEqual(np.argmax(result[1].samples), np.argmax(result[2].samples)) self.plot(*result) + 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.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")) @@ -72,3 +78,9 @@ class AlignTests(Plottable): self.assertEqual(np.argmax(result[0].samples), np.argmax(result[1].samples)) self.assertEqual(np.argmax(result[1].samples), np.argmax(result[2].samples)) self.plot(*result) + + 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.plot(*result_other) diff --git a/test/sca/test_combine.py b/test/sca/test_combine.py index a35772b..c48c7f8 100644 --- a/test/sca/test_combine.py +++ b/test/sca/test_combine.py @@ -1,7 +1,7 @@ from unittest import TestCase import numpy as np -from pyecsca.sca import Trace, CombinedTrace, average, conditional_average, standard_deviation +from pyecsca.sca import Trace, CombinedTrace, average, conditional_average, standard_deviation, add, subtract class CombineTests(TestCase): @@ -33,3 +33,18 @@ class CombineTests(TestCase): result = standard_deviation(self.a, self.b) self.assertIsInstance(result, CombinedTrace) self.assertEqual(len(result.samples), 2) + + def test_add(self): + self.assertIsNone(add()) + result = add(self.a, self.b) + self.assertIsInstance(result, CombinedTrace) + self.assertEqual(result.samples[0], 50) + self.assertEqual(result.samples[1], 122) + np.testing.assert_equal(self.a.samples, add(self.a).samples) + + def test_subtract(self): + result = subtract(self.a, self.b) + self.assertIsInstance(result, CombinedTrace) + self.assertEqual(result.samples[0], -10) + self.assertEqual(result.samples[1], 38) + diff --git a/test/sca/test_plot.py b/test/sca/test_plot.py index 5b65c87..2cd6b11 100644 --- a/test/sca/test_plot.py +++ b/test/sca/test_plot.py @@ -2,6 +2,7 @@ from os import getenv 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) @@ -27,6 +28,7 @@ class PlotTests(Plottable): if getenv("PYECSCA_TEST_PLOTS") is None: return hv.extension("matplotlib") + mpl.use("agg") fig = plot_trace(self.trace1) save_figure_png(fig, self.get_fname()) diff --git a/test/sca/test_target.py b/test/sca/test_target.py index 6c56d97..3652309 100644 --- a/test/sca/test_target.py +++ b/test/sca/test_target.py @@ -39,6 +39,22 @@ class BinaryTargetTests(TestCase): self.assertEqual(resp["r"].data, "01020304") target.disconnect() + def test_debug(self): + target_path = join(dirname(realpath(__file__)), "..", "data", "target.py") + target = TestTarget(["python", target_path], debug_output=True) + target.connect() + target.send_cmd(SimpleSerialMessage("d", ""), 500) + target.disconnect() + + def test_no_connection(self): + target_path = join(dirname(realpath(__file__)), "..", "data", "target.py") + target = TestTarget(target_path) + with self.assertRaises(ValueError): + target.write(bytes([1,2,3,4])) + with self.assertRaises(ValueError): + target.read(5) + target.disconnect() + class ECTesterTargetTests(TestCase): reader: Optional[str] = None |
