diff options
Diffstat (limited to 'test')
| -rw-r--r-- | test/ec/test_context.py | 17 | ||||
| -rw-r--r-- | test/ec/test_countermeasures.py | 168 | ||||
| -rw-r--r-- | test/sca/test_epa.py | 82 | ||||
| -rw-r--r-- | test/sca/test_rpa.py | 2 | ||||
| -rw-r--r-- | test/sca/test_rpa_context.py | 2 |
5 files changed, 239 insertions, 32 deletions
diff --git a/test/ec/test_context.py b/test/ec/test_context.py index e82f852..eeb2365 100644 --- a/test/ec/test_context.py +++ b/test/ec/test_context.py @@ -1,6 +1,13 @@ import pytest -from pyecsca.ec.context import local, DefaultContext, Node, PathContext, Action +from pyecsca.ec.context import ( + local, + DefaultContext, + Node, + PathContext, + Action, + compound, +) from pyecsca.ec.key_generation import KeyGeneration from pyecsca.ec.mod import RandomModAction from pyecsca.ec.mult import LTRMultiplier, ScalarMultiplicationAction @@ -124,6 +131,14 @@ def test_multiple_enter_no_copy(mult): assert len(ctx1.actions) == len(ctx2.actions) +def test_compound(mult): + one = DefaultContext() + other = DefaultContext() + with local(compound(one, other)): + mult.multiply(59) + assert len(one.actions) == len(other.actions) + + def test_path(mult, secp128r1): with local(PathContext([0, 1])) as ctx: key_generator = KeyGeneration(mult, secp128r1, True) diff --git a/test/ec/test_countermeasures.py b/test/ec/test_countermeasures.py index 28d3f33..7bd36db 100644 --- a/test/ec/test_countermeasures.py +++ b/test/ec/test_countermeasures.py @@ -1,5 +1,6 @@ from itertools import product from copy import copy +from typing import cast import pytest @@ -12,25 +13,43 @@ from pyecsca.ec.countermeasures import ( PointBlinding, MultPointBlinding, ) +from pyecsca.ec.formula import AdditionFormula, DoublingFormula, NegationFormula, ScalingFormula from pyecsca.ec.mod import mod -from pyecsca.ec.mult import * +from pyecsca.ec.mult import ( + LTRMultiplier, + RTLMultiplier, + BinaryNAFMultiplier, + WindowNAFMultiplier, + WindowBoothMultiplier, + SimpleLadderMultiplier, + FixedWindowLTRMultiplier, + SlidingWindowMultiplier, + FullPrecompMultiplier, + BGMWMultiplier, + CombMultiplier, + CoronMultiplier, + AccumulationOrder, + ProcessingDirection, + FakeAdditionFormula, + FakeNegationFormula, +) from pyecsca.sca.re.rpa import multiple_graph @pytest.fixture(params=["add-1998-cmo-2", "add-2015-rcb"]) def add(secp128r1, request): - return secp128r1.curve.coordinate_model.formulas[request.param] + return cast(AdditionFormula, secp128r1.curve.coordinate_model.formulas[request.param]) @pytest.fixture(params=["dbl-1998-cmo-2", "dbl-2015-rcb"]) def dbl(secp128r1, request): - return secp128r1.curve.coordinate_model.formulas[request.param] + return cast(DoublingFormula, secp128r1.curve.coordinate_model.formulas[request.param]) @pytest.fixture() def mults(secp128r1, add, dbl): - neg = secp128r1.curve.coordinate_model.formulas["neg"] - scale = secp128r1.curve.coordinate_model.formulas["z"] + neg = cast(NegationFormula, secp128r1.curve.coordinate_model.formulas["neg"]) + scale = cast(ScalingFormula, secp128r1.curve.coordinate_model.formulas["z"]) ltr_options = { "always": (True, False), @@ -310,7 +329,7 @@ def test_mult_point_blinding(mults, secp128r1, num): EuclideanSplitting, BrumleyTuveri, PointBlinding, - MultPointBlinding + MultPointBlinding, ), repeat=2, ), @@ -371,7 +390,7 @@ def test_combination(scalar, one, two, secp128r1): EuclideanSplitting, BrumleyTuveri, PointBlinding, - MultPointBlinding + MultPointBlinding, ), repeat=2, ), @@ -385,7 +404,7 @@ def test_combination_multiples(scalar, one, two, secp128r1): for i in range(2**two.nmults): bits = format(i, f"0{two.nmults}b") - def partial(*args, **kwargs): + def mult_partial(*args, **kwargs): mult = LTRMultiplier(*args, **kwargs) add = FakeAdditionFormula(secp128r1.curve.coordinate_model) @@ -413,9 +432,138 @@ def test_combination_multiples(scalar, one, two, secp128r1): dlog = 1 else: dlog = None - precomp_ctx, full_ctx, out = multiple_graph(scalar, secp128r1, LTRMultiplier, partial, dlog=dlog) + precomp_ctx, full_ctx, out = multiple_graph( + scalar, secp128r1, LTRMultiplier, mult_partial, dlog=dlog + ) assert out is not None - assert mod(full_ctx.points[out], secp128r1.order) == mod(scalar, secp128r1.order) + assert mod(full_ctx.points[out], secp128r1.order) == mod( + scalar, secp128r1.order + ) + + +@pytest.mark.parametrize( + "scalar", + [ + 3253857902090173296443513219124437746, + 1234567893141592653589793238464338327, + 86728612699079982903603364383639280149, + 60032993417060801067503559426926851620, + ], +) +@pytest.mark.parametrize( + "ctr", + ( + GroupScalarRandomization, + AdditiveSplitting, + MultiplicativeSplitting, + EuclideanSplitting, + BrumleyTuveri, + PointBlinding, + MultPointBlinding, + ), +) +@pytest.mark.parametrize( + "mult_ident", + ( + (BinaryNAFMultiplier, {}), + (WindowNAFMultiplier, {"width": 5}), + (WindowBoothMultiplier, {"width": 5}), + (SlidingWindowMultiplier, {"width": 2}), + ), + ids=lambda ident: ident[0].__name__ + + ",".join(f"{k}={v}" for k, v in ident[1].items()), +) +def test_precomp_multiples(scalar, ctr, mult_ident, secp128r1): + if ctr == PointBlinding: + pytest.xfail("PointBlinding will never work with multiple graphs.") + mult_cls, mult_kws = mult_ident + + def mult_partial(*args, **kwargs): + mult = mult_cls(*args, **kwargs, **mult_kws) + return ( + ctr.from_single(mult) + if ctr is not MultPointBlinding + else ctr.from_single( + mult, + neg=FakeNegationFormula(secp128r1.curve.coordinate_model), + ) + ) + + if ctr == MultPointBlinding: + dlog = 1 + else: + dlog = None + + precomp_ctx, full_ctx, out = multiple_graph( + scalar, secp128r1, mult_cls, mult_partial, dlog=dlog + ) + # These multipliers have precomputation, so they should have points in precomp_ctx + assert precomp_ctx.points + assert precomp_ctx.precomp + for pt in precomp_ctx.points: + assert pt in full_ctx.points + + +@pytest.mark.parametrize( + "scalar", + [ + 3253857902090173296443513219124437746, + 1234567893141592653589793238464338327, + 86728612699079982903603364383639280149, + 60032993417060801067503559426926851620, + ], +) +@pytest.mark.parametrize( + "ctr", + ( + GroupScalarRandomization, + AdditiveSplitting, + MultiplicativeSplitting, + EuclideanSplitting, + BrumleyTuveri, + PointBlinding, + MultPointBlinding, + ), +) +def test_cached_multiples_init(scalar, ctr, secp128r1, mocker): + if ctr == PointBlinding: + pytest.xfail("PointBlinding will never work with multiple graphs.") + init = mocker.patch( + "pyecsca.ec.mult.binary.LTRMultiplier.init", + side_effect=LTRMultiplier.init, + autospec=True, + ) + + called_partial = 0 + + def mult_partial(*args, **kwargs): + nonlocal called_partial + called_partial += 1 + + mults = [] + for _ in range(ctr.nmults): + mult = LTRMultiplier(*args, **kwargs) + # print(f"Created mult {id(mult)}") + mults.append(mult) + + return ( + ctr(*mults) + if ctr is not MultPointBlinding + else ctr(*mults, neg=FakeNegationFormula(secp128r1.curve.coordinate_model)) + ) + + if ctr == MultPointBlinding: + dlog = 1 + else: + dlog = None + multiple_graph(scalar, secp128r1, LTRMultiplier, mult_partial, dlog=dlog) + # print(f"Partial calls {called_partial}") + assert called_partial == 1 + # print(f"Init calls {init.call_count}") + assert init.call_count == ctr.nmults + for c in init.call_args_list: + # print(f"Init mult {id(c.args[0])}") + pass @pytest.mark.parametrize( diff --git a/test/sca/test_epa.py b/test/sca/test_epa.py index cab2f03..0975e35 100644 --- a/test/sca/test_epa.py +++ b/test/sca/test_epa.py @@ -1,12 +1,20 @@ import random +import sys from functools import partial +import networkx as nx import pytest +from matplotlib import pyplot as plt + from pyecsca.ec.coordinates import EFDCoordinateModel +from pyecsca.ec.curve import EllipticCurve +from pyecsca.ec.mod import mod +from pyecsca.ec.model import ShortWeierstrassModel +from pyecsca.ec.params import Point, InfinityPoint from pyecsca.ec.mult import * from pyecsca.sca.re.rpa import multiple_graph, multiples_from_graph -from pyecsca.sca.re.epa import errors_out, graph_to_check_inputs +from pyecsca.sca.re.epa import errors_out, graph_to_check_inputs, graph_plot_prepare def test_errors_out(secp128r1): @@ -139,7 +147,7 @@ def test_errors_out_precomp(secp128r1): use_multiply=False, ) assert set(affine_multiples) == set(precomp_ctx.precomp.keys()) - assert set(add_multiples) == {(1, 2), (3, 2)} + assert set(add_multiples) == {(1, 2)} # Here we check all, during both precomp and final multiply. affine_multiples = [] @@ -161,7 +169,7 @@ def test_errors_out_precomp(secp128r1): } # The add multiples should be the same as before, plus any inputs to add that happened # during the final multiply, there is only one, rest are doubles. - assert set(add_multiples) == {(1, 2), (3, 2), (16, -1)} + assert set(add_multiples) == {(1, 2), (16, -1)} # Now check just the multiply with all. affine_multiples = [] @@ -320,6 +328,38 @@ def mult(secp128r1, request): return mult_class, partial(mult_class, **mult_kwargs) +@pytest.fixture() +def toy_params(): + model = ShortWeierstrassModel() + coords = model.coordinates["projective"] + p = 0xCB5E1D94A6168511 + a = mod(0xB166CA7D2DFBF69F, p) + b = mod(0x855BB40CB6937C4B, p) + gx = mod(0x253B2638BD13D6F4, p) + gy = mod(0x1E91A1A182287E71, p) + + infty = InfinityPoint(coords) + 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, 0xCB5E1D94601A3AC5, 1) + + +@pytest.mark.skipif(sys.version_info < (3, 11), reason="Requires Python 3.11 or higher (networkx >= 3.5)") +def test_plot(toy_params, mult, plot_path): + mult_class, mult_factory = mult + precomp_ctx, full_ctx, out = multiple_graph( + scalar=15546875464546546545644687 % toy_params.order, + params=toy_params, + mult_class=mult_class, + mult_factory=mult_factory, + ) + fig, ax = plt.subplots(figsize=[60, 10]) + graph = graph_plot_prepare(precomp_ctx, full_ctx, out) + nx.display(graph, canvas=ax, node_size=700) + fig.savefig(str(plot_path()) + ".png") + plt.close() + + def test_independent_check_inputs(secp128r1, mult): """ Check that the set of check inputs is constant if (use_init = True, use_multiply = False) for all scalars @@ -354,13 +394,22 @@ def test_independent_check_inputs(secp128r1, mult): last_check_inputs = check_inputs -@pytest.mark.parametrize("check_condition,precomp_to_affine,multiples_kind", [ - ("all", True, "all"), - ("all", False, "all"), - ("necessary", True, "precomp+necessary"), - ("necessary", False, "necessary"), -]) -def test_consistency_multiples(secp128r1, mult, check_condition, precomp_to_affine, multiples_kind): +@pytest.mark.parametrize( + "check_condition,precomp_to_affine,multiples_kind", + [ + ("all", True, "all"), + ("all", False, "all"), + ("necessary", True, "precomp+necessary"), + ("necessary", False, "necessary"), + ], +) +def test_consistency_multiples( + secp128r1, + mult, + check_condition, + precomp_to_affine, + multiples_kind, +): """ Test consistency between the graph_to_check_inputs and multiples_computed functions for the same error model """ @@ -379,21 +428,19 @@ def test_consistency_multiples(secp128r1, mult, check_condition, precomp_to_affi out, check_condition=check_condition, precomp_to_affine=precomp_to_affine, - use_init=True, - use_multiply=True, ) # Now map the check inputs to the set of multiples they cover multiples_from_check_inputs = set() - for k, in check_inputs.get("neg", []): + for (k,) in check_inputs.get("neg", []): multiples_from_check_inputs.add(k) multiples_from_check_inputs.add(-k) - for k, in check_inputs.get("affine", []): + for (k,) in check_inputs.get("affine", []): multiples_from_check_inputs.add(k) for k, l in check_inputs.get("add", []): multiples_from_check_inputs.add(k) multiples_from_check_inputs.add(l) multiples_from_check_inputs.add(k + l) - for k, in check_inputs.get("dbl", []): + for (k,) in check_inputs.get("dbl", []): multiples_from_check_inputs.add(k) multiples_from_check_inputs.add(2 * k) # Multiples computed removes the zero @@ -401,9 +448,6 @@ def test_consistency_multiples(secp128r1, mult, check_condition, precomp_to_affi # Now compute the multiples via the other function to compare. multiples = multiples_from_graph( - precomp_ctx, - full_ctx, - out, - kind=multiples_kind + precomp_ctx, full_ctx, out, kind=multiples_kind ) assert multiples_from_check_inputs == multiples diff --git a/test/sca/test_rpa.py b/test/sca/test_rpa.py index e92ec44..6497ba2 100644 --- a/test/sca/test_rpa.py +++ b/test/sca/test_rpa.py @@ -94,7 +94,7 @@ def test_multiples_no_multiply(rpa_params): wnaf = partial(WindowNAFMultiplier, width=4) multiples = multiples_computed(78699, rpa_params, WindowNAFMultiplier, wnaf, True, False) - assert multiples == {1, 2, 3, 5, 7, 9} + assert multiples == {1, 2, 3, 5, 7} def test_multiples_bnaf(rpa_params): diff --git a/test/sca/test_rpa_context.py b/test/sca/test_rpa_context.py index c50b420..515a1a2 100644 --- a/test/sca/test_rpa_context.py +++ b/test/sca/test_rpa_context.py @@ -79,7 +79,7 @@ def test_precomp(secp128r1, add, dbl, neg, scale): with local(MultipleContext()) as ctx: wnaf.init(secp128r1, secp128r1.generator) muls = list(ctx.points.values()) - assert muls == [1, 0, 2, 3, 5] + assert muls == [1, 0, 2, 3] def test_window(secp128r1, add, dbl, neg): |
