aboutsummaryrefslogtreecommitdiffhomepage
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/ec/test_context.py17
-rw-r--r--test/ec/test_countermeasures.py168
-rw-r--r--test/sca/test_epa.py82
-rw-r--r--test/sca/test_rpa.py2
-rw-r--r--test/sca/test_rpa_context.py2
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):