diff options
| author | J08nY | 2023-08-09 17:19:55 +0200 |
|---|---|---|
| committer | J08nY | 2023-08-09 17:19:55 +0200 |
| commit | 21fa65c287fc3e830f169f610f643975a4d93966 (patch) | |
| tree | cf1f4146390bc8b40eed5aa1025caaae23f3a3d4 | |
| parent | 5309bf2ace118a507aa17c0a7b9a7b7bb109b682 (diff) | |
| download | pyecsca-21fa65c287fc3e830f169f610f643975a4d93966.tar.gz pyecsca-21fa65c287fc3e830f169f610f643975a4d93966.tar.zst pyecsca-21fa65c287fc3e830f169f610f643975a4d93966.zip | |
Move sca tests to pytest.
| -rw-r--r-- | pyecsca/misc/__init__.py | 2 | ||||
| -rw-r--r-- | pyecsca/sca/trace_set/inspector.py | 4 | ||||
| -rw-r--r-- | test/conftest.py | 18 | ||||
| -rw-r--r-- | test/ec/conftest.py | 18 | ||||
| -rwxr-xr-x | test/ec/perf_mult.py | 7 | ||||
| -rw-r--r-- | test/ec/test_mod.py | 6 | ||||
| -rw-r--r-- | test/ec/test_point.py | 2 | ||||
| -rw-r--r-- | test/sca/conftest.py | 43 | ||||
| -rw-r--r-- | test/sca/test_align.py | 246 | ||||
| -rw-r--r-- | test/sca/test_combine.py | 118 | ||||
| -rw-r--r-- | test/sca/test_edit.py | 76 | ||||
| -rw-r--r-- | test/sca/test_filter.py | 65 | ||||
| -rw-r--r-- | test/sca/test_leakage_models.py | 170 | ||||
| -rw-r--r-- | test/sca/test_match.py | 131 | ||||
| -rw-r--r-- | test/sca/test_plot.py | 57 | ||||
| -rw-r--r-- | test/sca/test_process.py | 86 | ||||
| -rw-r--r-- | test/sca/test_rpa.py | 190 | ||||
| -rw-r--r-- | test/sca/test_rpa_context.py | 106 | ||||
| -rw-r--r-- | test/sca/test_sampling.py | 295 | ||||
| -rw-r--r-- | test/sca/test_stacked_combine.py | 164 | ||||
| -rw-r--r-- | test/sca/test_target.py | 854 | ||||
| -rw-r--r-- | test/sca/test_test.py | 62 | ||||
| -rw-r--r-- | test/sca/test_trace.py | 12 | ||||
| -rw-r--r-- | test/sca/test_traceset.py | 200 | ||||
| -rw-r--r-- | test/sca/test_zvp.py | 30 | ||||
| -rw-r--r-- | test/sca/utils.py | 42 |
26 files changed, 1518 insertions, 1486 deletions
diff --git a/pyecsca/misc/__init__.py b/pyecsca/misc/__init__.py index f093677..243fc08 100644 --- a/pyecsca/misc/__init__.py +++ b/pyecsca/misc/__init__.py @@ -1 +1 @@ -"""package for miscellaneous things.""" +"""Package for miscellaneous things.""" diff --git a/pyecsca/sca/trace_set/inspector.py b/pyecsca/sca/trace_set/inspector.py index 2c9d581..9e18b04 100644 --- a/pyecsca/sca/trace_set/inspector.py +++ b/pyecsca/sca/trace_set/inspector.py @@ -146,7 +146,7 @@ class InspectorTraceSet(TraceSet): } @classmethod - def read(cls, input: Union[str, Path, bytes, BinaryIO]) -> "TraceSet": + def read(cls, input: Union[str, Path, bytes, BinaryIO]) -> "InspectorTraceSet": """ Read Inspector trace set from file path, bytes or file-like object. @@ -207,7 +207,7 @@ class InspectorTraceSet(TraceSet): return result, tags @classmethod - def inplace(cls, input: Union[str, Path, bytes, BinaryIO]) -> "TraceSet": + def inplace(cls, input: Union[str, Path, bytes, BinaryIO]) -> "InspectorTraceSet": raise NotImplementedError def write(self, output: Union[str, Path, BinaryIO]): diff --git a/test/conftest.py b/test/conftest.py new file mode 100644 index 0000000..5c3f855 --- /dev/null +++ b/test/conftest.py @@ -0,0 +1,18 @@ +import pytest + +from pyecsca.ec.params import get_params, DomainParameters + + +@pytest.fixture(scope="session") +def secp128r1() -> DomainParameters: + return get_params("secg", "secp128r1", "projective") + + +@pytest.fixture(scope="session") +def curve25519() -> DomainParameters: + return get_params("other", "Curve25519", "xz") + + +@pytest.fixture(scope="session") +def ed25519() -> DomainParameters: + return get_params("other", "Ed25519", "projective") diff --git a/test/ec/conftest.py b/test/ec/conftest.py index 5c3f855..e69de29 100644 --- a/test/ec/conftest.py +++ b/test/ec/conftest.py @@ -1,18 +0,0 @@ -import pytest - -from pyecsca.ec.params import get_params, DomainParameters - - -@pytest.fixture(scope="session") -def secp128r1() -> DomainParameters: - return get_params("secg", "secp128r1", "projective") - - -@pytest.fixture(scope="session") -def curve25519() -> DomainParameters: - return get_params("other", "Curve25519", "xz") - - -@pytest.fixture(scope="session") -def ed25519() -> DomainParameters: - return get_params("other", "Ed25519", "projective") diff --git a/test/ec/perf_mult.py b/test/ec/perf_mult.py index 51fb5a9..521f5e7 100755 --- a/test/ec/perf_mult.py +++ b/test/ec/perf_mult.py @@ -1,6 +1,9 @@ #!/usr/bin/env python +from typing import cast + import click +from pyecsca.ec.formula import AdditionFormula, DoublingFormula from pyecsca.ec.mod import has_gmp from pyecsca.ec.mult import LTRMultiplier from pyecsca.ec.params import get_params @@ -29,8 +32,8 @@ def main(profiler, mod, operations, directory): cfg.ec.mod_implementation = mod p256 = get_params("secg", "secp256r1", "projective") coords = p256.curve.coordinate_model - add = coords.formulas["add-2016-rcb"] - dbl = coords.formulas["dbl-2016-rcb"] + add = cast(AdditionFormula, coords.formulas["add-2016-rcb"]) + dbl = cast(DoublingFormula, coords.formulas["dbl-2016-rcb"]) mult = LTRMultiplier(add, dbl) click.echo( f"Profiling {operations} {p256.curve.prime.bit_length()}-bit scalar multiplication executions..." diff --git a/test/ec/test_mod.py b/test/ec/test_mod.py index 29b818e..55522b0 100644 --- a/test/ec/test_mod.py +++ b/test/ec/test_mod.py @@ -48,8 +48,7 @@ def test_inverse(): p = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF assert Mod( 0x702BDAFD3C1C837B23A1CB196ED7F9FADB333C5CFE4A462BE32ADCD67BFB6AC1, p - ).inverse() == \ - Mod(0x1CB2E5274BBA085C4CA88EEDE75AE77949E7A410C80368376E97AB22EB590F9D, p) + ).inverse() == Mod(0x1CB2E5274BBA085C4CA88EEDE75AE77949E7A410C80368376E97AB22EB590F9D, p) with pytest.raises(NonInvertibleError): Mod(0, p).inverse() with pytest.raises(NonInvertibleError): @@ -78,8 +77,7 @@ def test_sqrt(): p = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF assert Mod( 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC, p - ).sqrt() in \ - ( + ).sqrt() in ( 0x9ADD512515B70D9EC471151C1DEC46625CD18B37BDE7CA7FB2C8B31D7033599D, 0x6522AED9EA48F2623B8EEAE3E213B99DA32E74C9421835804D374CE28FCCA662, ) diff --git a/test/ec/test_point.py b/test/ec/test_point.py index b09ca30..4a5a6f6 100644 --- a/test/ec/test_point.py +++ b/test/ec/test_point.py @@ -105,7 +105,7 @@ def test_equals(secp128r1, coords): ) assert pt.equals(other) assert pt != other - assert not pt.equals(2) + assert not pt.equals(2) # type: ignore assert pt != 2 assert not pt.equals(third) assert pt != third diff --git a/test/sca/conftest.py b/test/sca/conftest.py new file mode 100644 index 0000000..a7a21d6 --- /dev/null +++ b/test/sca/conftest.py @@ -0,0 +1,43 @@ +from typing import Dict + +import pytest +from importlib_resources import files, as_file + +import matplotlib.pyplot as plt + +from pyecsca.sca import Trace + +cases: Dict[str, int] = {} + + +@pytest.fixture() +def plot_name(request): + def namer(): + test_name = f"{request.module.__name__}.{request.node.name}" + case_id = cases.setdefault(test_name, 0) + 1 + cases[test_name] = case_id + return test_name + str(case_id) + return namer + + +@pytest.fixture() +def plot_path(plot_name): + def namer(): + with as_file(files("test").joinpath("plots", plot_name())) as fname: + return fname + return namer + + +@pytest.fixture() +def plot(plot_path): + def plotter(*traces: Trace, **kwtraces: Trace): + fig = plt.figure() + ax = fig.add_subplot(111) + for i, trace in enumerate(traces): + ax.plot(trace.samples, label=str(i)) + for name, trace in kwtraces.items(): + ax.plot(trace.samples, label=name) + ax.legend(loc="best") + fname = plot_path() + plt.savefig(fname.parent / (fname.name + ".png")) + return plotter diff --git a/test/sca/test_align.py b/test/sca/test_align.py index db3874e..ad45d86 100644 --- a/test/sca/test_align.py +++ b/test/sca/test_align.py @@ -11,143 +11,139 @@ from pyecsca.sca import ( InspectorTraceSet, ) import test.data.sca -from .utils import Plottable -class AlignTests(Plottable): - def test_align(self): - 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, - ) - 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")), +def test_align(): + 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, + ) + assert result is not None + assert 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")), + ) + assert len(offsets) == 2 + assert offsets[0] == 0 + assert offsets[1] == 3 + + +@pytest.mark.slow +def test_large_align(): + with as_file(files(test.data.sca).joinpath("example.trs")) as path: + example = InspectorTraceSet.read(path) + result, _ = align_correlation( + *example, reference_offset=100000, reference_length=20000, max_offset=15000 ) - self.assertEqual(len(offsets), 2) - self.assertEqual(offsets[0], 0) - self.assertEqual(offsets[1], 3) + assert result is not None - @pytest.mark.slow - def test_large_align(self): - with as_file(files(test.data.sca).joinpath("example.trs")) as path: - example = InspectorTraceSet.read(path) - result, _ = align_correlation( - *example, reference_offset=100000, reference_length=20000, max_offset=15000 - ) - self.assertIsNotNone(result) - @pytest.mark.slow - def test_large_dtw_align(self): - with as_file(files(test.data.sca).joinpath("example.trs")) as path: - example = InspectorTraceSet.read(path) - result = align_dtw(*example[:5]) - self.assertIsNotNone(result) +@pytest.mark.slow +def test_large_dtw_align(): + with as_file(files(test.data.sca).joinpath("example.trs")) as path: + example = InspectorTraceSet.read(path) + result = align_dtw(*example[:5]) + assert result is not None - 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") - ) - a = Trace(first_arr) - b = Trace(second_arr) - result, _ = 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") - ) - a = Trace(first_arr) - b = Trace(second_arr) - result, _ = align_sad( - a, b, reference_offset=2, reference_length=5, max_offset=3 - ) - self.assertEqual(len(result), 2) +def test_peak_align(): + 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, _ = align_peaks( + a, b, reference_offset=2, reference_length=5, max_offset=3 + ) + assert np.argmax(result[0].samples) == np.argmax(result[1].samples) - 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"), - ) - a = Trace(first_arr) - b = Trace(second_arr) - c = Trace(third_arr) - result = align_dtw_scale(a, b, c) - 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) +def test_sad_align(): + 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, _ = align_sad( + a, b, reference_offset=2, reference_length=5, max_offset=3 + ) + assert len(result) == 2 - 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(plot): + 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) + result = align_dtw(a, b, c) - 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"), - ) - a = Trace(first_arr) - b = Trace(second_arr) - c = Trace(third_arr) - result = align_dtw(a, b, c) + assert np.argmax(result[0].samples) == np.argmax(result[1].samples) + assert np.argmax(result[1].samples) == np.argmax(result[2].samples) + plot(*result) - 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) - result_other = align_dtw(a, b, c, fast=False) + assert np.argmax(result_other[0].samples) == np.argmax(result_other[1].samples) + assert np.argmax(result_other[1].samples) == np.argmax(result_other[2].samples) + plot(*result_other) - 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_scale(plot): + 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) + result = align_dtw_scale(a, b, c) + + assert np.argmax(result[0].samples) == np.argmax(result[1].samples) + assert np.argmax(result[1].samples) == np.argmax(result[2].samples) + plot(*result) + + result_other = align_dtw_scale(a, b, c, fast=False) + + assert np.argmax(result_other[0].samples) == np.argmax(result_other[1].samples) + assert np.argmax(result_other[1].samples) == np.argmax(result_other[2].samples) + plot(*result_other) diff --git a/test/sca/test_combine.py b/test/sca/test_combine.py index 953b4bf..7e62780 100644 --- a/test/sca/test_combine.py +++ b/test/sca/test_combine.py @@ -1,6 +1,7 @@ -from unittest import TestCase - +from collections import namedtuple import numpy as np +import pytest + from pyecsca.sca import ( Trace, CombinedTrace, @@ -14,65 +15,68 @@ from pyecsca.sca import ( ) -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"}) - self.c = Trace(np.array([78, 56], dtype=np.dtype("i1")), {"data": b"\x00"}) +@pytest.fixture() +def data(): + Data = namedtuple("Data", ["a", "b", "c"]) + return Data(a=Trace(np.array([20, 80], dtype=np.dtype("i1")), {"data": b"\xff"}), + b=Trace(np.array([30, 42], dtype=np.dtype("i1")), {"data": b"\xff"}), + c=Trace(np.array([78, 56], dtype=np.dtype("i1")), {"data": b"\x00"})) + + +def test_average(data): + assert average() is None + result = average(data.a, data.b) + assert result is not None + assert isinstance(result, CombinedTrace) + assert len(result.samples) == 2 + assert result.samples[0] == 25 + assert result.samples[1] == 61 + + +def test_conditional_average(data): + result = conditional_average(data.a, data.b, data.c, condition=lambda trace: trace.meta["data"] == b"\xff", ) + assert isinstance(result, CombinedTrace) + assert len(result.samples) == 2 + assert result.samples[0] == 25 + assert result.samples[1] == 61 + + +def test_standard_deviation(data): + assert standard_deviation() is None + result = standard_deviation(data.a, data.b) + assert isinstance(result, CombinedTrace) + assert len(result.samples) == 2 + - def test_average(self): - self.assertIsNone(average()) - result = average(self.a, self.b) - self.assertIsNotNone(result) - self.assertIsInstance(result, CombinedTrace) - self.assertEqual(len(result.samples), 2) - self.assertEqual(result.samples[0], 25) - self.assertEqual(result.samples[1], 61) +def test_variance(data): + assert variance() is None + result = variance(data.a, data.b) + assert isinstance(result, CombinedTrace) + assert len(result.samples) == 2 - def test_conditional_average(self): - 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) - self.assertEqual(result.samples[1], 61) - def test_standard_deviation(self): - self.assertIsNone(standard_deviation()) - result = standard_deviation(self.a, self.b) - self.assertIsInstance(result, CombinedTrace) - self.assertEqual(len(result.samples), 2) +def test_average_and_variance(data): + assert average_and_variance() is None + mean, var = average_and_variance(data.a, data.b) + assert isinstance(mean, CombinedTrace) + assert isinstance(var, CombinedTrace) + assert len(mean.samples) == 2 + assert len(var.samples) == 2 + assert mean == average(data.a, data.b) + assert var == variance(data.a, data.b) - def test_variance(self): - self.assertIsNone(variance()) - result = variance(self.a, self.b) - self.assertIsInstance(result, CombinedTrace) - self.assertEqual(len(result.samples), 2) - def test_average_and_variance(self): - self.assertIsNone(average_and_variance()) - mean, var = average_and_variance(self.a, self.b) - self.assertIsInstance(mean, CombinedTrace) - self.assertIsInstance(var, CombinedTrace) - self.assertEqual(len(mean.samples), 2) - self.assertEqual(len(var.samples), 2) - self.assertEqual(mean, average(self.a, self.b)) - self.assertEqual(var, variance(self.a, self.b)) +def test_add(data): + assert add() is None + result = add(data.a, data.b) + assert isinstance(result, CombinedTrace) + assert result.samples[0] == 50 + assert result.samples[1] == 122 + np.testing.assert_equal(data.a.samples, add(data.a).samples) - 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) +def test_subtract(data): + result = subtract(data.a, data.b) + assert isinstance(result, CombinedTrace) + assert result.samples[0] == -10 + assert result.samples[1] == 38 diff --git a/test/sca/test_edit.py b/test/sca/test_edit.py index 282e62e..db80393 100644 --- a/test/sca/test_edit.py +++ b/test/sca/test_edit.py @@ -1,48 +1,50 @@ -from unittest import TestCase - import numpy as np +import pytest 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"))) +@pytest.fixture() +def trace(): + return Trace(np.array([10, 20, 30, 40, 50], dtype=np.dtype("i1"))) + + +def test_trim(trace): + result = trim(trace, 2) + assert result is not None + np.testing.assert_equal( + result.samples, np.array([30, 40, 50], dtype=np.dtype("i1")) + ) + + result = trim(trace, end=3) + assert result is not None + np.testing.assert_equal( + result.samples, np.array([10, 20, 30], 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")) - ) + with pytest.raises(ValueError): + trim(trace, 5, 1) - result = trim(self._trace, end=3) - self.assertIsNotNone(result) - np.testing.assert_equal( - result.samples, np.array([10, 20, 30], dtype=np.dtype("i1")) - ) - with self.assertRaises(ValueError): - trim(self._trace, 5, 1) +def test_reverse(trace): + result = reverse(trace) + assert result is not None + np.testing.assert_equal( + result.samples, np.array([50, 40, 30, 20, 10], dtype=np.dtype("i1")) + ) - 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")) - ) - 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")), - ) +def test_pad(trace): + result = pad(trace, 2) + assert result is not None + 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")), - ) + result = pad(trace, (1, 3)) + assert result is not None + 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 91f037c..d937f3a 100644 --- a/test/sca/test_filter.py +++ b/test/sca/test_filter.py @@ -1,4 +1,6 @@ import numpy as np +import pytest + from pyecsca.sca import ( Trace, filter_lowpass, @@ -6,39 +8,42 @@ from pyecsca.sca import ( 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, - ) +@pytest.fixture() +def trace(): + return 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, + ) + + +def test_lowpass(trace, plot): + result = filter_lowpass(trace, 100, 20) + assert result is not None + assert len(trace.samples) == len(result.samples) + plot(trace, result) + + +def test_highpass(trace, plot): + result = filter_highpass(trace, 128, 20) + assert result is not None + assert len(trace.samples) == len(result.samples) + plot(trace, result) - def test_lowpass(self): - result = filter_lowpass(self._trace, 100, 20) - self.assertIsNotNone(result) - self.assertEqual(len(self._trace.samples), len(result.samples)) - self.plot(self._trace, result) - def test_highpass(self): - result = filter_highpass(self._trace, 128, 20) - self.assertIsNotNone(result) - self.assertEqual(len(self._trace.samples), len(result.samples)) - self.plot(self._trace, result) +def test_bandpass(trace, plot): + result = filter_bandpass(trace, 128, 20, 60) + assert result is not None + assert len(trace.samples) == len(result.samples) + plot(trace, result) - def test_bandpass(self): - result = filter_bandpass(self._trace, 128, 20, 60) - self.assertIsNotNone(result) - self.assertEqual(len(self._trace.samples), len(result.samples)) - self.plot(self._trace, result) - def test_bandstop(self): - result = filter_bandstop(self._trace, 128, 20, 60) - self.assertIsNotNone(result) - self.assertEqual(len(self._trace.samples), len(result.samples)) - self.plot(self._trace, result) +def test_bandstop(trace, plot): + result = filter_bandstop(trace, 128, 20, 60) + assert result is not None + assert len(trace.samples) == len(result.samples) + plot(trace, result) diff --git a/test/sca/test_leakage_models.py b/test/sca/test_leakage_models.py index e9da42c..8be5704 100644 --- a/test/sca/test_leakage_models.py +++ b/test/sca/test_leakage_models.py @@ -1,119 +1,101 @@ -from unittest import TestCase - from pyecsca.ec.context import local, DefaultContext from pyecsca.ec.formula import FormulaAction, OpResult from pyecsca.ec.mod import Mod from pyecsca.ec.mult import LTRMultiplier from pyecsca.ec.op import OpType -from pyecsca.ec.params import get_params from pyecsca.sca.attack.leakage_model import Identity, Bit, Slice, HammingWeight, HammingDistance, BitLength +import pytest -class LeakageModelTests(TestCase): +def test_identity(): + val = Mod(3, 7) + lm = Identity() + assert lm(val) == 3 - def test_identity(self): - val = Mod(3, 7) - lm = Identity() - self.assertEqual(lm(val), 3) - def test_bit(self): - val = Mod(3, 7) - lm = Bit(0) - self.assertEqual(lm(val), 1) - lm = Bit(4) - self.assertEqual(lm(val), 0) - with self.assertRaises(ValueError): - Bit(-3) +def test_bit(): + val = Mod(3, 7) + lm = Bit(0) + assert lm(val) == 1 + lm = Bit(4) + assert lm(val) == 0 + with pytest.raises(ValueError): + Bit(-3) - def test_slice(self): - val = Mod(0b11110000, 0xf00) - lm = Slice(0, 4) - self.assertEqual(lm(val), 0) - lm = Slice(1, 5) - self.assertEqual(lm(val), 0b1000) - lm = Slice(4, 8) - self.assertEqual(lm(val), 0b1111) - with self.assertRaises(ValueError): - Slice(7, 1) - def test_hamming_weight(self): - val = Mod(0b11110000, 0xf00) - lm = HammingWeight() - self.assertEqual(lm(val), 4) +def test_slice(): + val = Mod(0b11110000, 0xf00) + lm = Slice(0, 4) + assert lm(val) == 0 + lm = Slice(1, 5) + assert lm(val) == 0b1000 + lm = Slice(4, 8) + assert lm(val) == 0b1111 + with pytest.raises(ValueError): + Slice(7, 1) - def test_hamming_distance(self): - a = Mod(0b11110000, 0xf00) - b = Mod(0b00010000, 0xf00) - lm = HammingDistance() - self.assertEqual(lm(a, b), 3) - def test_bit_length(self): - a = Mod(0b11110000, 0xf00) - lm = BitLength() - self.assertEqual(lm(a), 8) +def test_hamming_weight(): + val = Mod(0b11110000, 0xf00) + lm = HammingWeight() + assert lm(val) == 4 -class ModelTraceTests(TestCase): +def test_hamming_distance(): + a = Mod(0b11110000, 0xf00) + b = Mod(0b00010000, 0xf00) + lm = HammingDistance() + assert lm(a, b) == 3 - def setUp(self): - self.secp128r1 = get_params("secg", "secp128r1", "projective") - self.base = self.secp128r1.generator - self.coords = self.secp128r1.curve.coordinate_model - self.add = self.coords.formulas["add-1998-cmo"] - self.dbl = self.coords.formulas["dbl-1998-cmo"] - self.neg = self.coords.formulas["neg"] - self.scale = self.coords.formulas["z"] - def test_mult_hw(self): - scalar = 0x123456789 - mult = LTRMultiplier( - self.add, - self.dbl, - self.scale, - always=True, - complete=False, - short_circuit=True, - ) - with local(DefaultContext()) as ctx: - mult.init(self.secp128r1, self.base) - mult.multiply(scalar) +def test_bit_length(): + a = Mod(0b11110000, 0xf00) + lm = BitLength() + assert lm(a) == 8 - lm = HammingWeight() - trace = [] - def callback(action): - if isinstance(action, FormulaAction): - for intermediate in action.op_results: - leak = lm(intermediate.value) - trace.append(leak) +@pytest.fixture() +def context(secp128r1): + scalar = 0x123456789 + mult = LTRMultiplier( + secp128r1.curve.coordinate_model.formulas["add-1998-cmo"], + secp128r1.curve.coordinate_model.formulas["dbl-1998-cmo"], + secp128r1.curve.coordinate_model.formulas["z"], + always=True, + complete=False, + short_circuit=True, + ) + with local(DefaultContext()) as ctx: + mult.init(secp128r1, secp128r1.generator) + mult.multiply(scalar) + return ctx + - ctx.actions.walk(callback) - self.assertGreater(len(trace), 0) +def test_mult_hw(context): + lm = HammingWeight() + trace = [] - def test_mult_hd(self): - scalar = 0x123456789 - mult = LTRMultiplier( - self.add, - self.dbl, - self.scale, - always=True, - complete=False, - short_circuit=True, - ) - with local(DefaultContext()) as ctx: - mult.init(self.secp128r1, self.base) - mult.multiply(scalar) + def callback(action): + if isinstance(action, FormulaAction): + for intermediate in action.op_results: + leak = lm(intermediate.value) + trace.append(leak) - lm = HammingDistance() - trace = [] + context.actions.walk(callback) + assert len(trace) > 0 - def callback(action): - if isinstance(action, FormulaAction): - for intermediate in action.op_results: - if intermediate.op == OpType.Mult: - values = list(map(lambda v: v.value if isinstance(v, OpResult) else v, intermediate.parents)) - leak = lm(*values) - trace.append(leak) - ctx.actions.walk(callback) - self.assertGreater(len(trace), 0) +def test_mult_hd(context): + lm = HammingDistance() + trace = [] + + def callback(action): + if isinstance(action, FormulaAction): + for intermediate in action.op_results: + if intermediate.op == OpType.Mult: + values = list(map(lambda v: v.value if isinstance(v, OpResult) else v, intermediate.parents)) + leak = lm(*values) + trace.append(leak) + + context.actions.walk(callback) + assert len(trace) > 0 diff --git a/test/sca/test_match.py b/test/sca/test_match.py index d39e8c3..03d11c4 100644 --- a/test/sca/test_match.py +++ b/test/sca/test_match.py @@ -1,72 +1,71 @@ import numpy as np from pyecsca.sca import Trace, match_pattern, match_part, pad -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( - [0, 1, 3, 1, 2, -2, -3, 1, 15, 12, -10, 0, 13, 17, -1, 0, 3, 1], - 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_simple_match(plot): + 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, + ) + filtered = match_part(base, 7, 9) + assert filtered == [7] + 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, - ) - 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)), - ) + +def test_multiple_match(plot): + 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) + assert filtered == [7, 19] + 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 7d2cec0..2722ba3 100644 --- a/test/sca/test_plot.py +++ b/test/sca/test_plot.py @@ -1,8 +1,8 @@ -from os import getenv - import numpy as np import holoviews as hv import matplotlib as mpl +import pytest + from pyecsca.sca.trace import Trace from pyecsca.sca.trace.plot import ( plot_trace, @@ -11,34 +11,35 @@ from pyecsca.sca.trace.plot import ( 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"))) +@pytest.fixture() +def trace1(): + return Trace(np.array([6, 7, 3, -2, 5, 1], dtype=np.dtype("i1"))) + + +@pytest.fixture() +def trace2(): + return Trace(np.array([2, 3, 7, 0, -1, 0], dtype=np.dtype("i1"))) + + +def test_html(trace1, trace2, plot_path): + hv.extension("bokeh") + fig = plot_trace(trace1) + save_figure(fig, str(plot_path())) + other = plot_traces(trace1, trace2) + save_figure(other, str(plot_path())) + - def test_html(self): - if getenv("PYECSCA_TEST_PLOTS") is None: - return - hv.extension("bokeh") - fig = plot_trace(self.trace1) - save_figure(fig, self.get_fname()) - other = plot_traces(self.trace1, self.trace2) - save_figure(other, self.get_fname()) +@pytest.mark.skip("Broken") +def test_png(trace1, plot_path): + hv.extension("matplotlib") + mpl.use("agg") + fig = plot_trace(trace1) + save_figure_png(fig, str(plot_path())) - def test_png(self): - 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()) - def test_svg(self): - if getenv("PYECSCA_TEST_PLOTS") is None: - return - hv.extension("matplotlib") - fig = plot_trace(self.trace1) - save_figure_svg(fig, self.get_fname()) +def test_svg(trace1, plot_path): + hv.extension("matplotlib") + fig = plot_trace(trace1) + save_figure_svg(fig, str(plot_path())) diff --git a/test/sca/test_process.py b/test/sca/test_process.py index fda8575..cf9fbbd 100644 --- a/test/sca/test_process.py +++ b/test/sca/test_process.py @@ -1,6 +1,6 @@ -from unittest import TestCase - import numpy as np +import pytest + from pyecsca.sca import ( Trace, absolute, @@ -14,48 +14,56 @@ from pyecsca.sca import ( ) -class ProcessTests(TestCase): - def setUp(self): - self._trace = Trace(np.array([30, -60, 145, 247], dtype=np.dtype("i2")), None) +@pytest.fixture() +def trace(): + return Trace(np.array([30, -60, 145, 247], dtype=np.dtype("i2")), None) + + +def test_absolute(trace): + result = absolute(trace) + assert result is not None + assert result.samples[1] == 60 + + +def test_invert(trace): + result = invert(trace) + assert result is not None + np.testing.assert_equal(result.samples, [-30, 60, -145, -247]) + + +def test_threshold(trace): + result = threshold(trace, 128) + assert result is not None + assert result.samples[0] == 0 + assert result.samples[2] == 1 + + +def test_rolling_mean(trace): + result = rolling_mean(trace, 2) + assert result is not None + assert len(result.samples) == 3 + assert result.samples[0] == -15 + assert result.samples[1] == 42 + assert result.samples[2] == 196 - def test_absolute(self): - result = absolute(self._trace) - self.assertIsNotNone(result) - self.assertEqual(result.samples[1], 60) - def test_invert(self): - result = invert(self._trace) - self.assertIsNotNone(result) - np.testing.assert_equal(result.samples, [-30, 60, -145, -247]) +def test_offset(trace): + result = offset(trace, 5) + assert result is not None + np.testing.assert_equal( + result.samples, np.array([35, -55, 150, 252], dtype=np.dtype("i2")) + ) - def test_threshold(self): - result = threshold(self._trace, 128) - self.assertIsNotNone(result) - self.assertEqual(result.samples[0], 0) - self.assertEqual(result.samples[2], 1) - def test_rolling_mean(self): - result = rolling_mean(self._trace, 2) - self.assertIsNotNone(result) - self.assertEqual(len(result.samples), 3) - self.assertEqual(result.samples[0], -15) - self.assertEqual(result.samples[1], 42) - self.assertEqual(result.samples[2], 196) +def test_recenter(trace): + assert recenter(trace) is not None - 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")) - ) - def test_recenter(self): - self.assertIsNotNone(recenter(self._trace)) +def test_normalize(trace): + result = normalize(trace) + assert result is not None - def test_normalize(self): - result = normalize(self._trace) - self.assertIsNotNone(result) - def test_normalize_wl(self): - result = normalize_wl(self._trace) - self.assertIsNotNone(result) +def test_normalize_wl(trace): + result = normalize_wl(trace) + assert result is not None diff --git a/test/sca/test_rpa.py b/test/sca/test_rpa.py index 2ab784a..f5dc7cc 100644 --- a/test/sca/test_rpa.py +++ b/test/sca/test_rpa.py @@ -1,8 +1,7 @@ import io from contextlib import redirect_stdout -from unittest import TestCase -from parameterized import parameterized +import pytest from pyecsca.ec.context import local from pyecsca.ec.model import ShortWeierstrassModel @@ -13,153 +12,84 @@ from pyecsca.ec.mult import ( RTLMultiplier, BinaryNAFMultiplier, WindowNAFMultiplier, - LadderMultiplier, SimpleLadderMultiplier, - DifferentialLadderMultiplier ) -from pyecsca.ec.params import get_params, DomainParameters +from pyecsca.ec.params import DomainParameters from pyecsca.ec.point import Point from pyecsca.sca.re.rpa import MultipleContext, rpa_point_0y, rpa_point_x0, rpa_distinguish -class MultipleContextTests(TestCase): - def setUp(self): - self.secp128r1 = get_params("secg", "secp128r1", "projective") - self.base = self.secp128r1.generator - self.coords = self.secp128r1.curve.coordinate_model - self.add = self.coords.formulas["add-1998-cmo"] - self.dbl = self.coords.formulas["dbl-1998-cmo"] - self.neg = self.coords.formulas["neg"] - self.scale = self.coords.formulas["z"] +@pytest.fixture() +def model(): + return ShortWeierstrassModel() - @parameterized.expand( - [ - ("5", 5), - ("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, - ) - with local(MultipleContext()) as ctx: - mult.init(self.secp128r1, self.base) - mult.multiply(scalar) - muls = list(ctx.points.values()) - self.assertEqual(muls[-1], scalar) - def test_precomp(self): - bnaf = BinaryNAFMultiplier(self.add, self.dbl, self.neg, self.scale) - with local(MultipleContext()) as ctx: - bnaf.init(self.secp128r1, self.base) - muls = list(ctx.points.values()) - self.assertListEqual(muls, [1, -1]) +@pytest.fixture() +def coords(model): + return model.coordinates["projective"] - wnaf = WindowNAFMultiplier(self.add, self.dbl, self.neg, 3, self.scale) - with local(MultipleContext()) as ctx: - wnaf.init(self.secp128r1, self.base) - muls = list(ctx.points.values()) - self.assertListEqual(muls, [1, 2, 3, 5]) - def test_window(self): - mult = WindowNAFMultiplier( - self.add, self.dbl, self.neg, 3, precompute_negation=True - ) - with local(MultipleContext()): - mult.init(self.secp128r1, self.base) - mult.multiply(5) +@pytest.fixture() +def add(coords): + return coords.formulas["add-2007-bl"] - def test_ladder(self): - curve25519 = get_params("other", "Curve25519", "xz") - base = curve25519.generator - coords = curve25519.curve.coordinate_model - ladd = coords.formulas["ladd-1987-m"] - dadd = coords.formulas["dadd-1987-m"] - dbl = coords.formulas["dbl-1987-m"] - scale = coords.formulas["scale"] - ladd_mult = LadderMultiplier(ladd, dbl, scale) - with local(MultipleContext()) as ctx: - ladd_mult.init(curve25519, base) - ladd_mult.multiply(1339278426732672313) - muls = list(ctx.points.values()) - self.assertEqual(muls[-2], 1339278426732672313) - dadd_mult = DifferentialLadderMultiplier(dadd, dbl, scale) - with local(MultipleContext()) as ctx: - dadd_mult.init(curve25519, base) - dadd_mult.multiply(1339278426732672313) - muls = list(ctx.points.values()) - self.assertEqual(muls[-2], 1339278426732672313) +@pytest.fixture() +def dbl(coords): + return coords.formulas["dbl-2007-bl"] -class RPATests(TestCase): - def setUp(self): - self.model = ShortWeierstrassModel() - self.coords = self.model.coordinates["projective"] - self.add = self.coords.formulas["add-2007-bl"] - self.dbl = self.coords.formulas["dbl-2007-bl"] - self.neg = self.coords.formulas["neg"] +@pytest.fixture() +def neg(coords): + return coords.formulas["neg"] - def test_x0_point(self): - p = 0x85d265945a4f5681 - a = Mod(0x7fc57b4110698bc0, p) - b = Mod(0x37113ea591b04527, p) - gx = Mod(0x80d2d78fddb97597, p) - gy = Mod(0x5586d818b7910930, p) - # (0x4880bcf620852a54, 0) RPA point - infty = Point(self.coords, X=Mod(0, p), Y=Mod(1, p), Z=Mod(0, p)) - g = Point(self.coords, X=gx, Y=gy, Z=Mod(1, p)) - curve = EllipticCurve(self.model, self.coords, p, infty, dict(a=a, b=b)) - params_full = DomainParameters(curve, g, 0x85d265932d90785c, 1) +@pytest.fixture() +def rpa_params(model, coords): + p = 0x85d265945a4f5681 + a = Mod(0x7fc57b4110698bc0, p) + b = Mod(0x37113ea591b04527, p) + gx = Mod(0x80d2d78fddb97597, p) + gy = Mod(0x5586d818b7910930, p) + # (0x4880bcf620852a54, 0) RPA point + # (0, 0x6bed3155c9ada064) RPA point - self.assertIsNotNone(rpa_point_x0(params_full)) + infty = Point(coords, X=Mod(0, p), Y=Mod(1, p), Z=Mod(0, p)) + g = Point(coords, X=gx, Y=gy, Z=Mod(1, p)) + curve = EllipticCurve(model, coords, p, infty, dict(a=a, b=b)) + return DomainParameters(curve, g, 0x85d265932d90785c, 1) - def test_0y_point(self): - p = 0x85d265945a4f5681 - a = Mod(0x7fc57b4110698bc0, p) - b = Mod(0x37113ea591b04527, p) - gx = Mod(0x80d2d78fddb97597, p) - gy = Mod(0x5586d818b7910930, p) - # (0, 0x6bed3155c9ada064) RPA point - infty = Point(self.coords, X=Mod(0, p), Y=Mod(1, p), Z=Mod(0, p)) - g = Point(self.coords, X=gx, Y=gy, Z=Mod(1, p)) - curve = EllipticCurve(self.model, self.coords, p, infty, dict(a=a, b=b)) - params_full = DomainParameters(curve, g, 0x85d265932d90785c, 1) +def test_x0_point(rpa_params): + res = rpa_point_x0(rpa_params) + assert res is not None + assert res.y == 0 - self.assertIsNotNone(rpa_point_0y(params_full)) - def test_distinguish(self): - secp128r1 = get_params("secg", "secp128r1", "projective") - multipliers = [LTRMultiplier(self.add, self.dbl, None, False, True, True), - LTRMultiplier(self.add, self.dbl, None, True, True, True), - RTLMultiplier(self.add, self.dbl, None, False, True), - RTLMultiplier(self.add, self.dbl, None, True, True), - SimpleLadderMultiplier(self.add, self.dbl, None, True, True), - BinaryNAFMultiplier(self.add, self.dbl, self.neg, None, True), - WindowNAFMultiplier(self.add, self.dbl, self.neg, 3, None, True), - WindowNAFMultiplier(self.add, self.dbl, self.neg, 4, None, True)] - for real_mult in multipliers: - def simulated_oracle(scalar, affine_point): - point = affine_point.to_model(secp128r1.curve.coordinate_model, secp128r1.curve) - with local(MultipleContext()) as ctx: - real_mult.init(secp128r1, point) - real_mult.multiply(scalar) - return any(map(lambda P: P.X == 0 or P.Y == 0, ctx.points.keys())) +def test_0y_point(rpa_params): + res = rpa_point_0y(rpa_params) + assert res is not None + assert res.x == 0 - with redirect_stdout(io.StringIO()): - result = rpa_distinguish(secp128r1, multipliers, simulated_oracle) - self.assertEqual(1, len(result)) - self.assertEqual(real_mult, result[0]) + +def test_distinguish(secp128r1, add, dbl, neg): + multipliers = [LTRMultiplier(add, dbl, None, False, True, True), + LTRMultiplier(add, dbl, None, True, True, True), + RTLMultiplier(add, dbl, None, False, True), + RTLMultiplier(add, dbl, None, True, True), + SimpleLadderMultiplier(add, dbl, None, True, True), + BinaryNAFMultiplier(add, dbl, neg, None, True), + WindowNAFMultiplier(add, dbl, neg, 3, None, True), + WindowNAFMultiplier(add, dbl, neg, 4, None, True)] + for real_mult in multipliers: + def simulated_oracle(scalar, affine_point): + point = affine_point.to_model(secp128r1.curve.coordinate_model, secp128r1.curve) + with local(MultipleContext()) as ctx: + real_mult.init(secp128r1, point) + real_mult.multiply(scalar) + return any(map(lambda P: P.X == 0 or P.Y == 0, ctx.points.keys())) + + with redirect_stdout(io.StringIO()): + result = rpa_distinguish(secp128r1, multipliers, simulated_oracle) + assert 1 == len(result) + assert real_mult == result[0] diff --git a/test/sca/test_rpa_context.py b/test/sca/test_rpa_context.py new file mode 100644 index 0000000..78191bc --- /dev/null +++ b/test/sca/test_rpa_context.py @@ -0,0 +1,106 @@ +from typing import cast + +import pytest + +from pyecsca.ec.context import local +from pyecsca.ec.formula import LadderFormula, DifferentialAdditionFormula, DoublingFormula, \ + ScalingFormula +from pyecsca.ec.mult import ( + LTRMultiplier, + BinaryNAFMultiplier, + WindowNAFMultiplier, + LadderMultiplier, + DifferentialLadderMultiplier +) +from pyecsca.sca.re.rpa import MultipleContext + + +@pytest.fixture() +def add(secp128r1): + return secp128r1.curve.coordinate_model.formulas["add-1998-cmo"] + + +@pytest.fixture() +def dbl(secp128r1): + return secp128r1.curve.coordinate_model.formulas["dbl-1998-cmo"] + + +@pytest.fixture() +def neg(secp128r1): + return secp128r1.curve.coordinate_model.formulas["neg"] + + +@pytest.fixture() +def scale(secp128r1): + return secp128r1.curve.coordinate_model.formulas["z"] + + +@pytest.mark.parametrize("name,scalar", + [ + ("5", 5), + ("10", 10), + ("2355498743", 2355498743), + ( + "325385790209017329644351321912443757746", + 325385790209017329644351321912443757746, + ), + ("13613624287328732", 13613624287328732), + ]) +def test_basic(secp128r1, add, dbl, scale, name, scalar): + mult = LTRMultiplier( + add, + dbl, + scale, + always=False, + complete=False, + short_circuit=True, + ) + with local(MultipleContext()) as ctx: + mult.init(secp128r1, secp128r1.generator) + mult.multiply(scalar) + muls = list(ctx.points.values()) + assert muls[-1] == scalar + + +def test_precomp(secp128r1, add, dbl, neg, scale): + bnaf = BinaryNAFMultiplier(add, dbl, neg, scale) + with local(MultipleContext()) as ctx: + bnaf.init(secp128r1, secp128r1.generator) + muls = list(ctx.points.values()) + assert muls == [1, -1] + + wnaf = WindowNAFMultiplier(add, dbl, neg, 3, scale) + with local(MultipleContext()) as ctx: + wnaf.init(secp128r1, secp128r1.generator) + muls = list(ctx.points.values()) + assert muls == [1, 2, 3, 5] + + +def test_window(secp128r1, add, dbl, neg): + mult = WindowNAFMultiplier( + add, dbl, neg, 3, precompute_negation=True + ) + with local(MultipleContext()): + mult.init(secp128r1, secp128r1.generator) + mult.multiply(5) + + +def test_ladder(curve25519): + base = curve25519.generator + coords = curve25519.curve.coordinate_model + ladd = cast(LadderFormula, coords.formulas["ladd-1987-m"]) + dadd = cast(DifferentialAdditionFormula, coords.formulas["dadd-1987-m"]) + dbl = cast(DoublingFormula, coords.formulas["dbl-1987-m"]) + scale = cast(ScalingFormula, coords.formulas["scale"]) + ladd_mult = LadderMultiplier(ladd, dbl, scale) + with local(MultipleContext()) as ctx: + ladd_mult.init(curve25519, base) + ladd_mult.multiply(1339278426732672313) + muls = list(ctx.points.values()) + assert muls[-2] == 1339278426732672313 + dadd_mult = DifferentialLadderMultiplier(dadd, dbl, scale) + with local(MultipleContext()) as ctx: + dadd_mult.init(curve25519, base) + dadd_mult.multiply(1339278426732672313) + muls = list(ctx.points.values()) + assert muls[-2] == 1339278426732672313 diff --git a/test/sca/test_sampling.py b/test/sca/test_sampling.py index fcebf2d..062e726 100644 --- a/test/sca/test_sampling.py +++ b/test/sca/test_sampling.py @@ -7,162 +7,159 @@ from pyecsca.sca import ( 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"))) +def test_downsample_average(): + trace = Trace(np.array([20, 40, 50, 50, 10], dtype=np.dtype("i1"))) + result = downsample_average(trace, 2) + assert result is not None + assert isinstance(result, Trace) + assert len(result.samples) == 2 + assert result.samples[0] == 30 + assert result.samples[1] == 50 - def test_downsample_average(self): - result = downsample_average(self._trace, 2) - self.assertIsNotNone(result) - self.assertIsInstance(result, Trace) - self.assertEqual(len(result.samples), 2) - self.assertEqual(result.samples[0], 30) - self.assertEqual(result.samples[1], 50) - def test_downsample_pick(self): - result = downsample_pick(self._trace, 2) - self.assertIsNotNone(result) - self.assertIsInstance(result, Trace) - self.assertEqual(len(result.samples), 3) - self.assertEqual(result.samples[0], 20) - self.assertEqual(result.samples[1], 50) +def test_downsample_pick(): + trace = Trace(np.array([20, 40, 50, 50, 10], dtype=np.dtype("i1"))) + result = downsample_pick(trace, 2) + assert result is not None + assert isinstance(result, Trace) + assert len(result.samples) == 3 + assert result.samples[0] == 20 + assert 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"), - ) - ) - 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] - ) - 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"), - ) +def test_downsample_max(): + 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], + ) + result = downsample_max(trace, 2) + assert result is not None + assert isinstance(result, Trace) + assert len(result.samples) == 15 + assert list(result) == [30, 55, 15, 35, 21, 10, -5, -8, -15, -21, -10, -5, -2, 8, 28] + + +def test_downsample_min(): + 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) + assert result is not None + assert isinstance(result, Trace) + assert len(result.samples) == 15 + assert 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"), - ) +def test_downsample_decimate(plot): + 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) - self.assertEqual(len(result.samples), 15) - self.plot(trace, result) + ) + result = downsample_decimate(trace, 2) + assert result is not None + assert isinstance(result, Trace) + assert len(result.samples) == 15 + plot(original=trace, result=result) diff --git a/test/sca/test_stacked_combine.py b/test/sca/test_stacked_combine.py index 6f8fe60..4ed8d35 100644 --- a/test/sca/test_stacked_combine.py +++ b/test/sca/test_stacked_combine.py @@ -1,4 +1,4 @@ -from unittest import TestCase +import pytest from numba import cuda import numpy as np @@ -15,98 +15,94 @@ TRACE_COUNT = 32 TRACE_LEN = 4 * TPB -class StackedCombineTests(TestCase): - def setUp(self): - if not cuda.is_available(): - self.skipTest("CUDA not available") - self.samples = np.random.rand(TRACE_COUNT, TRACE_LEN) - self.stacked_ts = StackedTraces(self.samples) - self.gpu_manager = GPUTraceManager(self.stacked_ts, TPB) +@pytest.fixture() +def samples(): + np.random.seed(0x1234) + return np.random.rand(TRACE_COUNT, TRACE_LEN) - def test_fromarray(self): - max_len = self.samples.shape[1] - min_len = max_len // 2 - jagged_samples = [ - t[min_len:np.random.randint(max_len)] - for t - in self.samples - ] - min_len = min(map(len, jagged_samples)) - stacked = StackedTraces.fromarray(jagged_samples) - self.assertIsInstance(stacked, StackedTraces) - self.assertTupleEqual( - stacked.samples.shape, - (self.samples.shape[0], min_len) - ) - self.assertTrue((stacked.samples == self.samples[:, :min_len]).all()) +@pytest.fixture() +def gpu_manager(samples): + if not cuda.is_available(): + pytest.skip("CUDA not available") + return GPUTraceManager(StackedTraces(samples), TPB) - def test_fromtraceset(self): - max_len = self.samples.shape[1] - min_len = max_len // 2 - traces = [ - Trace(t[min_len:np.random.randint(max_len)]) - for t - in self.samples - ] - tset = TraceSet(*traces) - min_len = min(map(len, traces)) - stacked = StackedTraces.fromtraceset(tset) - self.assertIsInstance(stacked, StackedTraces) - self.assertTupleEqual( - stacked.samples.shape, - (self.samples.shape[0], min_len) - ) - self.assertTrue((stacked.samples == self.samples[:, :min_len]).all()) +def test_fromarray(samples): + max_len = samples.shape[1] + min_len = max_len // 2 + jagged_samples = [ + t[min_len:np.random.randint(max_len)] + for t + in samples + ] + min_len = min(map(len, jagged_samples)) + stacked = StackedTraces.fromarray(jagged_samples) - def test_average(self): - avg_trace = self.gpu_manager.average() - avg_cmp: np.ndarray = np.average(self.samples, 0) + assert isinstance(stacked, StackedTraces) + assert stacked.samples.shape == \ + (samples.shape[0], min_len) + assert (stacked.samples == samples[:, :min_len]).all() - self.assertIsInstance(avg_trace, CombinedTrace) - self.assertTupleEqual( - avg_trace.samples.shape, - avg_cmp.shape - ) - self.assertTrue(all(np.isclose(avg_trace.samples, avg_cmp))) - def test_standard_deviation(self): - std_trace = self.gpu_manager.standard_deviation() - std_cmp: np.ndarray = np.std(self.samples, 0) +def test_fromtraceset(samples): + max_len = samples.shape[1] + min_len = max_len // 2 + traces = [ + Trace(t[min_len:np.random.randint(max_len)]) + for t + in samples + ] + tset = TraceSet(*traces) + min_len = min(map(len, traces)) + stacked = StackedTraces.fromtraceset(tset) - self.assertIsInstance(std_trace, CombinedTrace) - self.assertTupleEqual( - std_trace.samples.shape, - std_cmp.shape - ) - self.assertTrue(all(np.isclose(std_trace.samples, std_cmp))) + assert isinstance(stacked, StackedTraces) + assert stacked.samples.shape == \ + (samples.shape[0], min_len) + assert (stacked.samples == samples[:, :min_len]).all() - def test_variance(self): - var_trace = self.gpu_manager.variance() - var_cmp: np.ndarray = np.var(self.samples, 0) - self.assertIsInstance(var_trace, CombinedTrace) - self.assertTupleEqual( - var_trace.samples.shape, - var_cmp.shape - ) - self.assertTrue(all(np.isclose(var_trace.samples, var_cmp))) +def test_average(samples, gpu_manager): + avg_trace = gpu_manager.average() + avg_cmp: np.ndarray = np.average(samples, 0) - def test_average_and_variance(self): - avg_trace, var_trace = self.gpu_manager.average_and_variance() - avg_cmp: np.ndarray = np.average(self.samples, 0) - var_cmp: np.ndarray = np.var(self.samples, 0) + assert isinstance(avg_trace, CombinedTrace) + assert avg_trace.samples.shape == \ + avg_cmp.shape + assert all(np.isclose(avg_trace.samples, avg_cmp)) - self.assertIsInstance(avg_trace, CombinedTrace) - self.assertIsInstance(var_trace, CombinedTrace) - self.assertTupleEqual( - avg_trace.samples.shape, - avg_cmp.shape - ) - self.assertTupleEqual( - var_trace.samples.shape, - var_cmp.shape - ) - self.assertTrue(all(np.isclose(avg_trace.samples, avg_cmp))) - self.assertTrue(all(np.isclose(var_trace.samples, var_cmp))) + +def test_standard_deviation(samples, gpu_manager): + std_trace = gpu_manager.standard_deviation() + std_cmp: np.ndarray = np.std(samples, 0) + + assert isinstance(std_trace, CombinedTrace) + assert std_trace.samples.shape == \ + std_cmp.shape + assert all(np.isclose(std_trace.samples, std_cmp)) + + +def test_variance(samples, gpu_manager): + var_trace = gpu_manager.variance() + var_cmp: np.ndarray = np.var(samples, 0) + + assert isinstance(var_trace, CombinedTrace) + assert var_trace.samples.shape == \ + var_cmp.shape + assert all(np.isclose(var_trace.samples, var_cmp)) + + +def test_average_and_variance(samples, gpu_manager): + avg_trace, var_trace = gpu_manager.average_and_variance() + avg_cmp: np.ndarray = np.average(samples, 0) + var_cmp: np.ndarray = np.var(samples, 0) + + assert isinstance(avg_trace, CombinedTrace) + assert isinstance(var_trace, CombinedTrace) + assert avg_trace.samples.shape == \ + avg_cmp.shape + assert var_trace.samples.shape == \ + var_cmp.shape + assert all(np.isclose(avg_trace.samples, avg_cmp)) + assert all(np.isclose(var_trace.samples, var_cmp)) diff --git a/test/sca/test_target.py b/test/sca/test_target.py index 3ca92fb..4155b25 100644 --- a/test/sca/test_target.py +++ b/test/sca/test_target.py @@ -1,9 +1,8 @@ import io from contextlib import redirect_stdout from copy import copy -from os.path import realpath, dirname, join -from typing import Optional -from unittest import TestCase, SkipTest + +import pytest from importlib_resources import files, as_file from smartcard.pcsc.PCSCExceptions import BaseSCardException @@ -36,460 +35,473 @@ from pyecsca.sca.target.ectester import ( if has_pyscard: from pyecsca.sca.target.ectester import ECTesterTargetPCSC as ECTesterTarget else: - ECTesterTarget = None + from pyecsca.sca.target.ectester import ECTesterTarget class TestTarget(SimpleSerialTarget, BinaryTarget): - pass + __test__ = False + +def test_basic_target(): + with as_file(files(test.data.sca).joinpath("target.py")) as target_path: + target = TestTarget(["python", target_path]) + target.connect() + resp = target.send_cmd(SimpleSerialMessage("d", ""), 500) + assert "r" in resp + assert "z" in resp + assert resp["r"].data == "01020304" + target.disconnect() -class BinaryTargetTests(TestCase): - def test_basic_target(self): - with as_file(files(test.data.sca).joinpath("target.py")) as target_path: - target = TestTarget(["python", target_path]) + +def test_debug(): + with as_file(files(test.data.sca).joinpath("target.py")) as target_path: + target = TestTarget(["python", target_path], debug_output=True) + with redirect_stdout(io.StringIO()) as out: target.connect() - resp = target.send_cmd(SimpleSerialMessage("d", ""), 500) - self.assertIn("r", resp) - self.assertIn("z", resp) - self.assertEqual(resp["r"].data, "01020304") + target.send_cmd(SimpleSerialMessage("d", ""), 500) target.disconnect() + assert out.read() is not None - def test_debug(self): - with as_file(files(test.data.sca).joinpath("target.py")) as target_path: - target = TestTarget(["python", target_path], debug_output=True) - with redirect_stdout(io.StringIO()): - target.connect() - target.send_cmd(SimpleSerialMessage("d", ""), 500) - target.disconnect() - def test_no_connection(self): - with as_file(files(test.data.sca).joinpath("target.py")) as target_path: - target = TestTarget(str(target_path)) - with self.assertRaises(ValueError): - target.write(bytes([1, 2, 3, 4])) - with self.assertRaises(ValueError): - target.read(5) - target.disconnect() +def test_no_connection(): + with as_file(files(test.data.sca).joinpath("target.py")) as target_path: + target = TestTarget(str(target_path)) + with pytest.raises(ValueError): + target.write(bytes([1, 2, 3, 4])) + with pytest.raises(ValueError): + target.read(5) + target.disconnect() -class ECTesterTargetTests(TestCase): - reader: Optional[str] = None - target: Optional[ECTesterTarget] = None - secp256r1: DomainParameters - secp256r1_projective: DomainParameters +@pytest.fixture() +def secp256r1_affine(): + return get_params("secg", "secp256r1", "affine") - @classmethod - def setUpClass(cls): - if not has_pyscard: - return - from smartcard.System import readers - try: - rs = readers() - except BaseSCardException: - return - if not rs: - return - cls.reader = rs[0] - cls.secp256r1 = get_params("secg", "secp256r1", "affine") - cls.secp256r1_projective = get_params("secg", "secp256r1", "projective") +@pytest.fixture() +def secp256r1_projective(): + return get_params("secg", "secp256r1", "projective") - def setUp(self): - if not ECTesterTargetTests.reader: - raise SkipTest("No smartcard readers.") - self.target = ECTesterTarget(ECTesterTargetTests.reader) - self.target.connect() - if not self.target.select_applet(): - self.target.disconnect() - raise SkipTest("No applet in reader: {}".format(ECTesterTargetTests.reader)) - def tearDown(self): - self.target.cleanup() - self.target.disconnect() +@pytest.fixture() +def target(): + if not has_pyscard: + pytest.skip("No pyscard.") + from smartcard.System import readers + rs = None + try: + rs = readers() + except BaseSCardException as e: + pytest.skip(f"No reader found: {e}") + if not rs: + pytest.skip("No reader found") + reader = rs[0] + target: ECTesterTarget = ECTesterTarget(reader) + target.connect() + if not target.select_applet(): + target.disconnect() + pytest.skip(f"No applet in reader: {reader}") + yield target + target.cleanup() + target.disconnect() - def test_allocate(self): - ka_resp = self.target.allocate_ka(KeyAgreementEnum.ALG_EC_SVDP_DH) - 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, - ) - 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.assertTrue(set_resp.success) +def test_allocate(target): + ka_resp = target.allocate_ka(KeyAgreementEnum.ALG_EC_SVDP_DH) + assert ka_resp.success + sig_resp = target.allocate_sig(SignatureEnum.ALG_ECDSA_SHA) + assert sig_resp.success + key_resp = target.allocate( + KeypairEnum.KEYPAIR_LOCAL, + KeyBuildEnum.BUILD_KEYPAIR, + 256, + KeyClassEnum.ALG_EC_FP, + ) + assert key_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.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 - ) - generate_resp = self.target.generate(KeypairEnum.KEYPAIR_LOCAL) - self.assertTrue(generate_resp.success) +def test_set(target): + target.allocate( + KeypairEnum.KEYPAIR_LOCAL, + KeyBuildEnum.BUILD_KEYPAIR, + 256, + KeyClassEnum.ALG_EC_FP, + ) + set_resp = target.set( + KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP + ) + assert set_resp.success - def test_clear(self): - 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) - def test_cleanup(self): - cleanup_resp = self.target.cleanup() - self.assertTrue(cleanup_resp.success) +def test_set_explicit(target, secp256r1_affine): + target.allocate( + KeypairEnum.KEYPAIR_LOCAL, + KeyBuildEnum.BUILD_KEYPAIR, + 256, + KeyClassEnum.ALG_EC_FP, + ) + values = ECTesterTarget.encode_parameters( + ParameterEnum.DOMAIN_FP, secp256r1_affine + ) + set_resp = target.set( + KeypairEnum.KEYPAIR_LOCAL, + CurveEnum.external, + ParameterEnum.DOMAIN_FP, + values, + ) + assert set_resp.success - def test_info(self): - info_resp = self.target.info() - self.assertTrue(info_resp.success) - 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, - ) - self.assertTrue(allocate_resp.success) - dry_run_resp = self.target.run_mode(RunModeEnum.MODE_NORMAL) - self.assertTrue(dry_run_resp.success) +def test_generate(target): + target.allocate( + KeypairEnum.KEYPAIR_LOCAL, + KeyBuildEnum.BUILD_KEYPAIR, + 256, + KeyClassEnum.ALG_EC_FP, + ) + target.set( + KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP + ) + generate_resp = target.generate(KeypairEnum.KEYPAIR_LOCAL) + assert generate_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.generate(KeypairEnum.KEYPAIR_LOCAL) - 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 = self.secp256r1.curve.decode_point(pubkey_bytes) - 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), - ) - 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.assertTrue(export_resp.success) +def test_clear(target): + target.allocate( + KeypairEnum.KEYPAIR_LOCAL, + KeyBuildEnum.BUILD_KEYPAIR, + 256, + KeyClassEnum.ALG_EC_FP, + ) + clear_resp = target.clear(KeypairEnum.KEYPAIR_LOCAL) + assert clear_resp.success + + +def test_cleanup(target): + cleanup_resp = target.cleanup() + assert cleanup_resp.success + + +def test_info(target): + info_resp = target.info() + assert info_resp.success + + +def test_dry_run(target): + dry_run_resp = target.run_mode(RunModeEnum.MODE_DRY_RUN) + assert dry_run_resp.success + allocate_resp = target.allocate( + KeypairEnum.KEYPAIR_LOCAL, + KeyBuildEnum.BUILD_KEYPAIR, + 256, + KeyClassEnum.ALG_EC_FP, + ) + assert allocate_resp.success + dry_run_resp = target.run_mode(RunModeEnum.MODE_NORMAL) + assert dry_run_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.generate(KeypairEnum.KEYPAIR_LOCAL) - 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, - ) - self.assertTrue(transform_resp.success) - 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", - ) - 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.generate(KeypairEnum.KEYPAIR_BOTH) - 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 - ) - 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", +def test_export(target, secp256r1_affine): + target.allocate( + KeypairEnum.KEYPAIR_LOCAL, + KeyBuildEnum.BUILD_KEYPAIR, + 256, + KeyClassEnum.ALG_EC_FP, + ) + target.set( + KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP + ) + target.generate(KeypairEnum.KEYPAIR_LOCAL) + export_public_resp = target.export( + KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PUBLIC, ParameterEnum.W + ) + assert export_public_resp.success + pubkey_bytes = export_public_resp.get_param( + KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.W + ) + pubkey = secp256r1_affine.curve.decode_point(pubkey_bytes) + export_privkey_resp = target.export( + KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PRIVATE, ParameterEnum.S + ) + assert export_privkey_resp.success + privkey = int.from_bytes( + export_privkey_resp.get_param(KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.S), + "big", + ) + assert pubkey == \ + secp256r1_affine.curve.affine_multiply(secp256r1_affine.generator, privkey) + + +def test_export_curve(target): + target.allocate( + KeypairEnum.KEYPAIR_LOCAL, + KeyBuildEnum.BUILD_KEYPAIR, + 256, + KeyClassEnum.ALG_EC_FP, + ) + target.set( + KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP + ) + export_resp = target.export( + KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PUBLIC, ParameterEnum.DOMAIN_FP + ) + assert export_resp.success + + +def test_transform(target): + target.allocate( + KeypairEnum.KEYPAIR_LOCAL, + KeyBuildEnum.BUILD_KEYPAIR, + 256, + KeyClassEnum.ALG_EC_FP, + ) + target.set( + KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP + ) + target.generate(KeypairEnum.KEYPAIR_LOCAL) + export_privkey_resp1 = 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 = target.transform( + KeypairEnum.KEYPAIR_LOCAL, + KeyEnum.PRIVATE, + ParameterEnum.S, + TransformationEnum.INCREMENT, + ) + assert transform_resp.success + export_privkey_resp2 = 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", + ) + assert privkey + 1 == privkey_new + + +def test_ecdh(target, secp256r1_affine, secp256r1_projective): + target.allocate_ka(KeyAgreementEnum.ALG_EC_SVDP_DH) + target.allocate( + KeypairEnum.KEYPAIR_BOTH, + KeyBuildEnum.BUILD_KEYPAIR, + 256, + KeyClassEnum.ALG_EC_FP, + ) + target.set( + KeypairEnum.KEYPAIR_BOTH, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP + ) + target.generate(KeypairEnum.KEYPAIR_BOTH) + ecdh_resp = target.ecdh( + KeypairEnum.KEYPAIR_LOCAL, + KeypairEnum.KEYPAIR_REMOTE, + True, + TransformationEnum.NONE, + KeyAgreementEnum.ALG_EC_SVDP_DH, + ) + assert ecdh_resp.success + export_public_resp = target.export( + KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PUBLIC, ParameterEnum.W + ) + pubkey_bytes = export_public_resp.get_param( + KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.W + ) + pubkey = secp256r1_affine.curve.decode_point(pubkey_bytes) + export_privkey_resp = target.export( + KeypairEnum.KEYPAIR_REMOTE, KeyEnum.PRIVATE, ParameterEnum.S + ) + privkey = Mod( + int.from_bytes( + export_privkey_resp.get_param( + KeypairEnum.KEYPAIR_REMOTE, ParameterEnum.S ), - self.secp256r1.curve.prime, - ) - pubkey_projective = pubkey.to_model( - self.secp256r1_projective.curve.coordinate_model, self.secp256r1.curve - ) + "big", + ), + secp256r1_affine.curve.prime, + ) + pubkey_projective = pubkey.to_model( + secp256r1_projective.curve.coordinate_model, secp256r1_affine.curve + ) - mult = LTRMultiplier( - 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) + mult = LTRMultiplier( + secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"], + secp256r1_projective.curve.coordinate_model.formulas["dbl-2016-rcb"], + ) + ecdh = ECDH_SHA1(mult, secp256r1_projective, pubkey_projective, privkey) + expected = ecdh.perform() + assert 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.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"], - ) - keygen = KeyGeneration(copy(mult), self.secp256r1_projective) - _, 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()), - ) - 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", +def test_ecdh_raw(target, secp256r1_projective): + target.allocate_ka(KeyAgreementEnum.ALG_EC_SVDP_DH) + target.allocate( + KeypairEnum.KEYPAIR_LOCAL, + KeyBuildEnum.BUILD_KEYPAIR, + 256, + KeyClassEnum.ALG_EC_FP, + ) + target.set( + KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP + ) + target.generate(KeypairEnum.KEYPAIR_LOCAL) + mult = LTRMultiplier( + secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"], + secp256r1_projective.curve.coordinate_model.formulas["dbl-2016-rcb"], + ) + keygen = KeyGeneration(copy(mult), secp256r1_projective) + _, pubkey_projective = keygen.generate() + + ecdh_resp = target.ecdh_direct( + KeypairEnum.KEYPAIR_LOCAL, + True, + TransformationEnum.NONE, + KeyAgreementEnum.ALG_EC_SVDP_DH, + bytes(pubkey_projective.to_affine()), + ) + assert ecdh_resp.success + export_privkey_resp = target.export( + KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PRIVATE, ParameterEnum.S + ) + privkey = Mod( + int.from_bytes( + export_privkey_resp.get_param( + KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.S ), - self.secp256r1.curve.prime, - ) + "big", + ), + secp256r1_projective.curve.prime, + ) - ecdh = ECDH_SHA1( - copy(mult), self.secp256r1_projective, pubkey_projective, privkey - ) - expected = ecdh.perform() - self.assertEqual(ecdh_resp.secret, expected) + ecdh = ECDH_SHA1( + copy(mult), secp256r1_projective, pubkey_projective, privkey + ) + expected = ecdh.perform() + assert 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.generate(KeypairEnum.KEYPAIR_LOCAL) - data = "Some text over here.".encode() - 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 - ) - pubkey = self.secp256r1.curve.decode_point(pubkey_bytes) - 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.assertTrue(ecdsa.verify_data(sig, data)) +def test_ecdsa(target, secp256r1_affine, secp256r1_projective): + target.allocate_sig(SignatureEnum.ALG_ECDSA_SHA) + target.allocate( + KeypairEnum.KEYPAIR_LOCAL, + KeyBuildEnum.BUILD_KEYPAIR, + 256, + KeyClassEnum.ALG_EC_FP, + ) + target.set( + KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP + ) + target.generate(KeypairEnum.KEYPAIR_LOCAL) + data = "Some text over here.".encode() + ecdsa_resp = target.ecdsa( + KeypairEnum.KEYPAIR_LOCAL, True, SignatureEnum.ALG_ECDSA_SHA, data + ) + assert ecdsa_resp.success + export_public_resp = target.export( + KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PUBLIC, ParameterEnum.W + ) + pubkey_bytes = export_public_resp.get_param( + KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.W + ) + pubkey = secp256r1_affine.curve.decode_point(pubkey_bytes) + pubkey_projective = pubkey.to_model( + secp256r1_projective.curve.coordinate_model, secp256r1_affine.curve + ) - 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.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 - ) - 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 - ) - pubkey = self.secp256r1.curve.decode_point(pubkey_bytes) - pubkey_projective = pubkey.to_model( - self.secp256r1_projective.curve.coordinate_model, self.secp256r1.curve - ) + sig = SignatureResult.from_DER(ecdsa_resp.signature) + mult = LTRMultiplier( + secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"], + secp256r1_projective.curve.coordinate_model.formulas["dbl-2016-rcb"], + ) + ecdsa = ECDSA_SHA1( + copy(mult), + secp256r1_projective, + secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"], + pubkey_projective, + ) + assert ecdsa.verify_data(sig, data) - 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.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 - ) - mult = LTRMultiplier( - 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, - ) - data = "Some text over here.".encode() - sig = ecdsa.sign_data(data) +def test_ecdsa_sign(target, secp256r1_affine, secp256r1_projective): + target.allocate_sig(SignatureEnum.ALG_ECDSA_SHA) + target.allocate( + KeypairEnum.KEYPAIR_LOCAL, + KeyBuildEnum.BUILD_KEYPAIR, + 256, + KeyClassEnum.ALG_EC_FP, + ) + target.set( + KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP + ) + target.generate(KeypairEnum.KEYPAIR_LOCAL) + data = "Some text over here.".encode() + ecdsa_resp = target.ecdsa_sign( + KeypairEnum.KEYPAIR_LOCAL, True, SignatureEnum.ALG_ECDSA_SHA, data + ) + assert ecdsa_resp.success + export_public_resp = target.export( + KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PUBLIC, ParameterEnum.W + ) + pubkey_bytes = export_public_resp.get_param( + KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.W + ) + pubkey = secp256r1_affine.curve.decode_point(pubkey_bytes) + pubkey_projective = pubkey.to_model( + secp256r1_projective.curve.coordinate_model, secp256r1_affine.curve + ) + + sig = SignatureResult.from_DER(ecdsa_resp.signature) + mult = LTRMultiplier( + secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"], + secp256r1_projective.curve.coordinate_model.formulas["dbl-2016-rcb"], + ) + ecdsa = ECDSA_SHA1( + copy(mult), + secp256r1_projective, + secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"], + pubkey_projective, + ) + assert ecdsa.verify_data(sig, data) + + +def test_ecdsa_verify(target, secp256r1_projective): + target.allocate_sig(SignatureEnum.ALG_ECDSA_SHA) + target.allocate( + KeypairEnum.KEYPAIR_LOCAL, + KeyBuildEnum.BUILD_KEYPAIR, + 256, + KeyClassEnum.ALG_EC_FP, + ) + target.set( + KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP + ) + mult = LTRMultiplier( + secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"], + secp256r1_projective.curve.coordinate_model.formulas["dbl-2016-rcb"], + ) + keygen = KeyGeneration(copy(mult), secp256r1_projective) + priv, pubkey_projective = keygen.generate() + target.set( + KeypairEnum.KEYPAIR_LOCAL, + CurveEnum.external, + ParameterEnum.W, + ECTesterTarget.encode_parameters( + ParameterEnum.W, pubkey_projective.to_affine() + ), + ) + ecdsa = ECDSA_SHA1( + copy(mult), + secp256r1_projective, + 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 - ) - self.assertTrue(ecdsa_resp.success) + ecdsa_resp = target.ecdsa_verify( + KeypairEnum.KEYPAIR_LOCAL, SignatureEnum.ALG_ECDSA_SHA, sig.to_DER(), data + ) + assert ecdsa_resp.success diff --git a/test/sca/test_test.py b/test/sca/test_test.py index 7b4f346..256e77d 100644 --- a/test/sca/test_test.py +++ b/test/sca/test_test.py @@ -1,43 +1,41 @@ -from unittest import TestCase - +from collections import namedtuple import numpy as np +import pytest 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"))) - self.c = Trace(np.array([78, 56], dtype=np.dtype("i1"))) - self.d = Trace(np.array([98, 36], dtype=np.dtype("i1"))) +@pytest.fixture() +def data(): + Data = namedtuple("Data", ["a", "b", "c", "d"]) + return Data(a=Trace(np.array([20, 80], dtype=np.dtype("i1"))), + b=Trace(np.array([30, 42], dtype=np.dtype("i1"))), + c=Trace(np.array([78, 56], dtype=np.dtype("i1"))), + d=Trace(np.array([98, 36], dtype=np.dtype("i1")))) + + +def test_welch_ttest(data): + assert welch_ttest([data.a, data.b], [data.c, data.d]) is not None + 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]) + ) - 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]) - ) + result = welch_ttest([a, b], [b, c], dof=True, p_value=True) + assert result is not None - result = welch_ttest([a, b], [b, c], dof=True, p_value=True) - self.assertIsNotNone(result) - def test_students_ttest(self): - self.assertIsNone(student_ttest([], [])) - self.assertIsNotNone(student_ttest([self.a, self.b], [self.c, self.d])) +def test_students_ttest(data): + assert student_ttest([], []) is None + assert student_ttest([data.a, data.b], [data.c, data.d]) is not None -class KolmogorovSmirnovTests(TestCase): - def test_ks_test(self): - self.assertIsNone(ks_test([], [])) +def test_ks_test(data): + assert ks_test([], []) is None - a = Trace(np.array([20, 80], dtype=np.dtype("i1"))) - b = Trace(np.array([30, 42], dtype=np.dtype("i1"))) - c = Trace(np.array([78, 56], dtype=np.dtype("i1"))) - d = Trace(np.array([98, 36], dtype=np.dtype("i1"))) - self.assertIsNotNone(ks_test([a, b], [c, d])) + assert ks_test([data.a, data.b], [data.c, data.d]) is not None diff --git a/test/sca/test_trace.py b/test/sca/test_trace.py index 93baa89..98818d3 100644 --- a/test/sca/test_trace.py +++ b/test/sca/test_trace.py @@ -1,11 +1,9 @@ -from unittest import TestCase import numpy as np 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) - self.assertIn("Trace", str(trace)) - self.assertIsNone(trace.trace_set) +def test_basic(): + trace = Trace(np.array([10, 15, 24], dtype=np.dtype("i1"))) + assert trace is not None + assert "Trace" in str(trace) + assert trace.trace_set is None diff --git a/test/sca/test_traceset.py b/test/sca/test_traceset.py index 55b9a0a..a338a9d 100644 --- a/test/sca/test_traceset.py +++ b/test/sca/test_traceset.py @@ -1,8 +1,9 @@ import os.path import shutil import tempfile + +import pytest from importlib_resources import files, as_file -from unittest import TestCase import numpy as np @@ -16,121 +17,126 @@ from pyecsca.sca import ( 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_KWARGS = {"num_traces": 3, "thingy": "abc"} + +@pytest.fixture() +def example_traces(): + return [ + 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"))), + ] + + +@pytest.fixture() +def example_kwargs(): + return {"num_traces": 3, "thingy": "abc"} -class TraceSetTests(TestCase): - def test_create(self): - self.assertIsNotNone(TraceSet()) - self.assertIsNotNone(InspectorTraceSet()) - self.assertIsNotNone(ChipWhispererTraceSet()) - self.assertIsNotNone(PickleTraceSet()) - self.assertIsNotNone(HDF5TraceSet()) +def test_create(): + assert TraceSet() is not None + assert InspectorTraceSet() is not None + assert ChipWhispererTraceSet() is not None + assert PickleTraceSet() is not None + assert HDF5TraceSet() is not None -class InspectorTraceSetTests(TestCase): - def test_load_fname(self): - with as_file(files(test.data.sca).joinpath("example.trs")) as path: - result = InspectorTraceSet.read(path) - self.assertIsNotNone(result) - self.assertEqual(result.global_title, "Example trace set") - self.assertEqual(len(result), 10) - self.assertEqual(len(list(result)), 10) - self.assertIn("InspectorTraceSet", str(result)) - self.assertIs(result[0].trace_set, result) - self.assertEqual(result.sampling_frequency, 12500000) +def test_trs_load_fname(): + with as_file(files(test.data.sca).joinpath("example.trs")) as path: + result = InspectorTraceSet.read(path) + assert result is not None + assert result.global_title == "Example trace set" + assert len(result) == 10 + assert len(list(result)) == 10 + assert "InspectorTraceSet" in str(result) + assert result[0].trace_set is result + assert result.sampling_frequency == 12500000 - def test_load_file(self): - with files(test.data.sca).joinpath("example.trs").open("rb") as f: - self.assertIsNotNone(InspectorTraceSet.read(f)) - def test_load_bytes(self): - with files(test.data.sca).joinpath("example.trs").open("rb") as f: - self.assertIsNotNone(InspectorTraceSet.read(f.read())) +def test_trs_load_file(): + with files(test.data.sca).joinpath("example.trs").open("rb") as f: + assert InspectorTraceSet.read(f) is not None - def test_save(self): - with as_file(files(test.data.sca).joinpath("example.trs")) as path: - trace_set = InspectorTraceSet.read(path) + +def test_trs_load_bytes(): + with files(test.data.sca).joinpath("example.trs").open("rb") as f: + assert InspectorTraceSet.read(f.read()) is not None + + +def test_trs_save(): + with as_file(files(test.data.sca).joinpath("example.trs")) as path: + trace_set = InspectorTraceSet.read(path) with tempfile.TemporaryDirectory() as dirname: path = os.path.join(dirname, "out.trs") trace_set.write(path) - self.assertTrue(os.path.exists(path)) - self.assertIsNotNone(InspectorTraceSet.read(path)) + assert os.path.exists(path) + assert InspectorTraceSet.read(path) is not None -class ChipWhispererTraceSetTests(TestCase): - def test_load_fname(self): - with as_file(files(test.data.sca).joinpath("config_chipwhisperer_.cfg")) as path: - # This will not work if the test package is not on the file system directly. - result = ChipWhispererTraceSet.read(path) - self.assertIsNotNone(result) - self.assertEqual(len(result), 2) +def test_cw_load_fname(): + with as_file(files(test.data.sca).joinpath("config_chipwhisperer_.cfg")) as path: + # This will not work if the test package is not on the file system directly. + result = ChipWhispererTraceSet.read(path) + assert result is not None + assert len(result) == 2 -class PickleTraceSetTests(TestCase): - def test_load_fname(self): - with as_file(files(test.data.sca).joinpath("test.pickle")) as path: - result = PickleTraceSet.read(path) - self.assertIsNotNone(result) +def test_pickle_load_fname(): + with as_file(files(test.data.sca).joinpath("test.pickle")) as path: + result = PickleTraceSet.read(path) + assert result is not None - def test_load_file(self): - with files(test.data.sca).joinpath("test.pickle").open("rb") as f: - self.assertIsNotNone(PickleTraceSet.read(f)) - def test_save(self): - trace_set = PickleTraceSet(*EXAMPLE_TRACES, **EXAMPLE_KWARGS) - with tempfile.TemporaryDirectory() as dirname: - path = os.path.join(dirname, "out.pickle") - trace_set.write(path) - self.assertTrue(os.path.exists(path)) - self.assertIsNotNone(PickleTraceSet.read(path)) +def test_pickle_load_file(): + with files(test.data.sca).joinpath("test.pickle").open("rb") as f: + assert PickleTraceSet.read(f) is not None -class HDF5TraceSetTests(TestCase): - def test_load_fname(self): - with as_file(files(test.data.sca).joinpath("test.h5")) as path: - result = HDF5TraceSet.read(path) - self.assertIsNotNone(result) +def test_pickle_save(example_traces, example_kwargs): + trace_set = PickleTraceSet(*example_traces, **example_kwargs) + with tempfile.TemporaryDirectory() as dirname: + path = os.path.join(dirname, "out.pickle") + trace_set.write(path) + assert os.path.exists(path) + assert PickleTraceSet.read(path) is not None - def test_load_file(self): - with files(test.data.sca).joinpath("test.h5").open("rb") as f: - self.assertIsNotNone(HDF5TraceSet.read(f)) - def test_inplace(self): - with tempfile.TemporaryDirectory() as dirname, as_file(files(test.data.sca).joinpath("test.h5")) as orig_path: - path = os.path.join(dirname, "test.h5") - shutil.copy(orig_path, 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"} - ) - trace_set.append(test_trace) - self.assertEqual(len(trace_set), 4) - trace_set.append(other_trace) - trace_set.remove(other_trace) - self.assertEqual(len(trace_set), 4) - trace_set.save() - trace_set.close() +def test_h5_load_fname(): + with as_file(files(test.data.sca).joinpath("test.h5")) as path: + result = HDF5TraceSet.read(path) + assert result is not None - test_set = HDF5TraceSet.read(path) - self.assertEqual(test_set.get(3), test_set[3]) - self.assertTrue(np.array_equal(test_set[3].samples, test_trace.samples)) - self.assertEqual(test_set[3].meta["thing"], test_trace.meta["thing"]) - self.assertEqual(test_set[3], test_trace) - def test_save(self): - trace_set = HDF5TraceSet(*EXAMPLE_TRACES, **EXAMPLE_KWARGS) - with tempfile.TemporaryDirectory() as dirname: - path = os.path.join(dirname, "out.h5") - trace_set.write(path) - self.assertTrue(os.path.exists(path)) - self.assertIsNotNone(HDF5TraceSet.read(path)) +def test_h5_load_file(): + with files(test.data.sca).joinpath("test.h5").open("rb") as f: + assert HDF5TraceSet.read(f) is not None + + +def test_h5_inplace(): + with tempfile.TemporaryDirectory() as dirname, as_file(files(test.data.sca).joinpath("test.h5")) as orig_path: + path = os.path.join(dirname, "test.h5") + shutil.copy(orig_path, path) + trace_set = HDF5TraceSet.inplace(path) + assert trace_set is not None + 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) + assert len(trace_set) == 4 + trace_set.append(other_trace) + trace_set.remove(other_trace) + assert len(trace_set) == 4 + trace_set.save() + trace_set.close() + test_set = HDF5TraceSet.read(path) + assert test_set.get(3) == test_set[3] + assert np.array_equal(test_set[3].samples, test_trace.samples) + assert test_set[3].meta["thing"] == test_trace.meta["thing"] + assert test_set[3] == test_trace + + +def test_h5_save(example_traces, example_kwargs): + trace_set = HDF5TraceSet(*example_traces, **example_kwargs) + with tempfile.TemporaryDirectory() as dirname: + path = os.path.join(dirname, "out.h5") + trace_set.write(path) + assert os.path.exists(path) + assert HDF5TraceSet.read(path) is not None diff --git a/test/sca/test_zvp.py b/test/sca/test_zvp.py index 837fb08..3b6b046 100644 --- a/test/sca/test_zvp.py +++ b/test/sca/test_zvp.py @@ -1,23 +1,13 @@ -from unittest import TestCase - -from pyecsca.ec.model import ShortWeierstrassModel -from pyecsca.ec.params import get_params from pyecsca.sca.re.zvp import unroll_formula -class ZVPTests(TestCase): - def setUp(self): - self.secp128r1 = get_params("secg", "secp128r1", "projective") - self.model = ShortWeierstrassModel() - self.coords = self.model.coordinates["projective"] - self.add = self.coords.formulas["add-2007-bl"] - self.dbl = self.coords.formulas["dbl-2007-bl"] - self.neg = self.coords.formulas["neg"] - - def test_unroll(self): - results = unroll_formula(self.add, 11) - self.assertIsNotNone(results) - results = unroll_formula(self.dbl, 11) - self.assertIsNotNone(results) - results = unroll_formula(self.neg, 11) - self.assertIsNotNone(results) +def test_unroll(secp128r1): + add = secp128r1.curve.coordinate_model.formulas["add-2007-bl"] + dbl = secp128r1.curve.coordinate_model.formulas["dbl-2007-bl"] + neg = secp128r1.curve.coordinate_model.formulas["neg"] + results = unroll_formula(add, 11) + assert results is not None + results = unroll_formula(dbl, 11) + assert results is not None + results = unroll_formula(neg, 11) + assert results is not None diff --git a/test/sca/utils.py b/test/sca/utils.py deleted file mode 100644 index ad77d21..0000000 --- a/test/sca/utils.py +++ /dev/null @@ -1,42 +0,0 @@ -from os import mkdir, getenv, getcwd -from os.path import join, exists, split -from typing import Dict -from unittest import TestCase - -import matplotlib.pyplot as plt - -from pyecsca.sca import Trace - -force_plot = True - - -cases: Dict[str, int] = {} - - -class Plottable(TestCase): - def get_dir(self): - if split(getcwd())[1] == "test": - directory = "plots" - else: - directory = join("test", "plots") - if not exists(directory): - mkdir(directory) - return directory - - def get_fname(self): - directory = self.get_dir() - case_id = cases.setdefault(self.id(), 0) + 1 - cases[self.id()] = case_id - return join(directory, self.id() + str(case_id)) - - def plot(self, *traces: Trace, **kwtraces: Trace): - if not force_plot and getenv("PYECSCA_TEST_PLOTS") is None: - return - fig = plt.figure() - ax = fig.add_subplot(111) - for i, trace in enumerate(traces): - ax.plot(trace.samples, label=str(i)) - for name, trace in kwtraces.items(): - ax.plot(trace.samples, label=name) - ax.legend(loc="best") - plt.savefig(self.get_fname() + ".png") |
