From 077bf996ef042244c9259d3b32e13f07c2f3f3ae Mon Sep 17 00:00:00 2001 From: J08nY Date: Tue, 8 Aug 2023 16:21:07 +0200 Subject: Move EC tests to pytest fully. --- pyproject.toml | 3 + test/ec/conftest.py | 18 ++ test/ec/test_configuration.py | 118 +++----- test/ec/test_context.py | 173 ++++++------ test/ec/test_curve.py | 343 ++++++++++++----------- test/ec/test_divpoly.py | 266 +++++++----------- test/ec/test_formula.py | 201 +++++++------- test/ec/test_key_agreement.py | 64 +++-- test/ec/test_key_generation.py | 37 ++- test/ec/test_mod.py | 383 +++++++++++++------------- test/ec/test_model.py | 13 +- test/ec/test_mult.py | 595 ++++++++++++++++++++-------------------- test/ec/test_naf.py | 9 +- test/ec/test_op.py | 61 ++-- test/ec/test_params.py | 240 ++++++++-------- test/ec/test_point.py | 272 +++++++++--------- test/ec/test_regress.py | 191 ++++++------- test/ec/test_signature.py | 171 ++++++------ test/ec/test_transformations.py | 57 ++-- test/ec/utils.py | 1 + 20 files changed, 1571 insertions(+), 1645 deletions(-) create mode 100644 test/ec/conftest.py diff --git a/pyproject.toml b/pyproject.toml index bfffb07..12b681c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -72,3 +72,6 @@ testpaths = ["test"] markers = [ "slow: marks tests as slow (deselect with '-m \"not slow\"')", ] +filterwarnings = [ + "ignore:Deprecated call to `pkg_resources.declare_namespace" +] diff --git a/test/ec/conftest.py b/test/ec/conftest.py new file mode 100644 index 0000000..5c3f855 --- /dev/null +++ b/test/ec/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/test_configuration.py b/test/ec/test_configuration.py index a90f3ce..17fb6f7 100644 --- a/test/ec/test_configuration.py +++ b/test/ec/test_configuration.py @@ -1,5 +1,3 @@ -from unittest import TestCase - import pytest from pyecsca.ec.configuration import ( @@ -15,86 +13,44 @@ from pyecsca.ec.model import ShortWeierstrassModel from pyecsca.ec.mult import LTRMultiplier -class ConfigurationTests(TestCase): - def base_independents(self): - return { - "hash_type": HashType.SHA1, - "mod_rand": RandomMod.SAMPLE, - "mult": Multiplication.BASE, - "sqr": Squaring.BASE, - "red": Reduction.BASE, - "inv": Inversion.GCD, - } +@pytest.fixture(scope="module") +def base_independents(): + return {"hash_type": HashType.SHA1, "mod_rand": RandomMod.SAMPLE, "mult": Multiplication.BASE, "sqr": Squaring.BASE, + "red": Reduction.BASE, "inv": Inversion.GCD, } + + +@pytest.mark.slow +def test_all(): + j = 0 + for _ in all_configurations(model=ShortWeierstrassModel()): + j += 1 + + +def test_weierstrass_projective(base_independents): + model = ShortWeierstrassModel() + coords = model.coordinates["projective"] + configs = list(all_configurations(model=model, coords=coords, **base_independents)) + assert len(configs) == 1960 - @pytest.mark.slow - def test_all(self): - j = 0 - for _ in all_configurations(model=ShortWeierstrassModel()): - j += 1 - def test_weierstrass_projective(self): - model = ShortWeierstrassModel() - coords = model.coordinates["projective"] - configs = list( - all_configurations(model=model, coords=coords, **self.base_independents()) - ) - self.assertEqual(len(configs), 1960) +def test_mult_class(base_independents): + model = ShortWeierstrassModel() + coords = model.coordinates["projective"] + scalarmult = LTRMultiplier + configs = list(all_configurations(model=model, coords=coords, scalarmult=scalarmult, **base_independents)) + assert len(configs) == 560 - def test_mult_class(self): - model = ShortWeierstrassModel() - coords = model.coordinates["projective"] - scalarmult = LTRMultiplier - configs = list( - all_configurations( - model=model, - coords=coords, - scalarmult=scalarmult, - **self.base_independents() - ) - ) - self.assertEqual(len(configs), 560) - def test_one(self): - model = ShortWeierstrassModel() - coords = model.coordinates["projective"] - scalarmult = { - "cls": LTRMultiplier, - "add": coords.formulas["add-1998-cmo"], - "dbl": coords.formulas["dbl-1998-cmo"], - "scl": None, - "always": True, - "complete": False, - "short_circuit": True, - } - configs = list( - all_configurations( - model=model, - coords=coords, - scalarmult=scalarmult, - **self.base_independents() - ) - ) - self.assertEqual(len(configs), 1) - scalarmult = LTRMultiplier( - coords.formulas["add-1998-cmo"], - coords.formulas["dbl-1998-cmo"], - None, - True, - False, - True, - ) - configs = list( - all_configurations( - model=model, - coords=coords, - scalarmult=scalarmult, - **self.base_independents() - ) - ) - self.assertEqual(len(configs), 1) - configs = list( - all_configurations( - model=model, scalarmult=scalarmult, **self.base_independents() - ) - ) - self.assertEqual(len(configs), 1) +def test_one(base_independents): + model = ShortWeierstrassModel() + coords = model.coordinates["projective"] + scalarmult = {"cls": LTRMultiplier, "add": coords.formulas["add-1998-cmo"], "dbl": coords.formulas["dbl-1998-cmo"], + "scl": None, "always": True, "complete": False, "short_circuit": True, } + configs = list(all_configurations(model=model, coords=coords, scalarmult=scalarmult, **base_independents)) + assert len(configs) == 1 + scalarmult = LTRMultiplier(coords.formulas["add-1998-cmo"], coords.formulas["dbl-1998-cmo"], None, True, False, + True, ) + configs = list(all_configurations(model=model, coords=coords, scalarmult=scalarmult, **base_independents)) + assert len(configs) == 1 + configs = list(all_configurations(model=model, scalarmult=scalarmult, **base_independents)) + assert len(configs) == 1 diff --git a/test/ec/test_context.py b/test/ec/test_context.py index 9cd74a3..9ec7962 100644 --- a/test/ec/test_context.py +++ b/test/ec/test_context.py @@ -1,4 +1,4 @@ -from unittest import TestCase +import pytest from pyecsca.ec.context import ( local, @@ -7,90 +7,95 @@ from pyecsca.ec.context import ( PathContext ) from pyecsca.ec.key_generation import KeyGeneration -from pyecsca.ec.params import get_params from pyecsca.ec.mod import RandomModAction from pyecsca.ec.mult import LTRMultiplier, ScalarMultiplicationAction -class TreeTests(TestCase): - def test_walk_by_key(self): - tree = Tree() - tree["a"] = Tree() - tree["a"]["1"] = Tree() - tree["a"]["2"] = Tree() - self.assertIn("a", tree) - self.assertIsInstance(tree.get_by_key([]), Tree) - self.assertIsInstance(tree.get_by_key(["a"]), Tree) - self.assertIsInstance(tree.get_by_key(["a", "1"]), Tree) - - def test_walk_by_index(self): - tree = Tree() - a = Tree() - tree["a"] = a - d = Tree() - b = Tree() - tree["a"]["d"] = d - tree["a"]["b"] = b - self.assertIn("a", tree) - with self.assertRaises(ValueError): - tree.get_by_index([]) - - self.assertEqual(tree.get_by_index([0]), ("a", a)) - self.assertEqual(tree.get_by_index([0, 0]), ("d", d)) - - def test_repr(self): - tree = Tree() - tree["a"] = Tree() - tree["a"]["1"] = Tree() - tree["a"]["2"] = Tree() - txt = tree.repr() - self.assertEqual(txt.count("\t"), 2) - self.assertEqual(txt.count("\n"), 3) - - -class ContextTests(TestCase): - def setUp(self): - self.secp128r1 = get_params("secg", "secp128r1", "projective") - self.base = self.secp128r1.generator - self.coords = self.secp128r1.curve.coordinate_model - self.mult = LTRMultiplier( - self.coords.formulas["add-1998-cmo"], - self.coords.formulas["dbl-1998-cmo"], - self.coords.formulas["z"], - always=True, - ) - self.mult.init(self.secp128r1, self.base) - - def test_null(self): - with local() as ctx: - self.mult.multiply(59) - self.assertIs(ctx, None) - - def test_default(self): - with local(DefaultContext()) as ctx: - result = self.mult.multiply(59) - self.assertEqual(len(ctx.actions), 1) - action = next(iter(ctx.actions.keys())) - self.assertIsInstance(action, ScalarMultiplicationAction) - self.assertEqual(result, action.result) - - def test_default_no_enter(self): - with local(DefaultContext()) as default, self.assertRaises(ValueError): - default.exit_action(RandomModAction(7)) - - def test_path(self): - with local(PathContext([0, 1])) as ctx: - key_generator = KeyGeneration(self.mult, self.secp128r1, True) - key_generator.generate() - self.assertIsInstance(ctx.value, ScalarMultiplicationAction) - with local(PathContext([0, 1, 7])) as ctx: - key_generator = KeyGeneration(self.mult, self.secp128r1, True) - key_generator.generate() - - def test_str(self): - with local(DefaultContext()) as default: - self.mult.multiply(59) - str(default) - str(default.actions) - with local(None): - self.mult.multiply(59) +def test_walk_by_key(): + tree = Tree() + tree["a"] = Tree() + tree["a"]["1"] = Tree() + tree["a"]["2"] = Tree() + assert "a" in tree + assert isinstance(tree.get_by_key([]), Tree) + assert isinstance(tree.get_by_key(["a"]), Tree) + assert isinstance(tree.get_by_key(["a", "1"]), Tree) + + +def test_walk_by_index(): + tree = Tree() + a = Tree() + tree["a"] = a + d = Tree() + b = Tree() + tree["a"]["d"] = d + tree["a"]["b"] = b + assert "a" in tree + with pytest.raises(ValueError): + tree.get_by_index([]) + + assert tree.get_by_index([0]) == ("a", a) + assert tree.get_by_index([0, 0]) == ("d", d) + + +def test_repr(): + tree = Tree() + tree["a"] = Tree() + tree["a"]["1"] = Tree() + tree["a"]["2"] = Tree() + txt = tree.repr() + assert txt.count("\t") == 2 + assert txt.count("\n") == 3 + + +@pytest.fixture() +def mult(secp128r1): + base = secp128r1.generator + coords = secp128r1.curve.coordinate_model + mult = LTRMultiplier( + coords.formulas["add-1998-cmo"], + coords.formulas["dbl-1998-cmo"], + coords.formulas["z"], + always=True, + ) + mult.init(secp128r1, base) + return mult + + +def test_null(mult): + with local() as ctx: + mult.multiply(59) + assert ctx is None + + +def test_default(mult): + with local(DefaultContext()) as ctx: + result = mult.multiply(59) + assert len(ctx.actions) == 1 + action = next(iter(ctx.actions.keys())) + assert isinstance(action, ScalarMultiplicationAction) + assert result == action.result + + +def test_default_no_enter(): + with local(DefaultContext()) as default, pytest.raises(ValueError): + default.exit_action(RandomModAction(7)) + + +def test_path(mult, secp128r1): + with local(PathContext([0, 1])) as ctx: + key_generator = KeyGeneration(mult, secp128r1, True) + key_generator.generate() + assert isinstance(ctx.value, ScalarMultiplicationAction) + with local(PathContext([0, 1, 7])): + key_generator = KeyGeneration(mult, secp128r1, True) + key_generator.generate() + + +def test_str(mult): + with local(DefaultContext()) as default: + mult.multiply(59) + assert str(default) is not None + assert str(default.actions) is not None + with local(None): + mult.multiply(59) diff --git a/test/ec/test_curve.py b/test/ec/test_curve.py index bc0cdaf..cd0639a 100644 --- a/test/ec/test_curve.py +++ b/test/ec/test_curve.py @@ -1,192 +1,187 @@ from binascii import unhexlify -from unittest import TestCase +import pytest from pyecsca.ec.coordinates import AffineCoordinateModel from pyecsca.ec.curve import EllipticCurve from pyecsca.ec.error import UnsatisfiedAssumptionError -from pyecsca.ec.params import get_params from pyecsca.ec.mod import Mod from pyecsca.ec.model import MontgomeryModel from pyecsca.ec.point import Point, InfinityPoint -class CurveTests(TestCase): - def setUp(self): - self.secp128r1 = get_params("secg", "secp128r1", "projective") - self.base = self.secp128r1.generator - self.affine_base = self.base.to_affine() - self.curve25519 = get_params("other", "Curve25519", "xz") - self.ed25519 = get_params("other", "Ed25519", "projective") - - def test_init(self): - with self.assertRaises(ValueError): - EllipticCurve( - MontgomeryModel(), - self.secp128r1.curve.coordinate_model, - 1, - InfinityPoint(self.secp128r1.curve.coordinate_model), - parameters={}, - ) - - with self.assertRaises(ValueError): - EllipticCurve( - self.secp128r1.curve.model, - self.secp128r1.curve.coordinate_model, - 15, - InfinityPoint(self.secp128r1.curve.coordinate_model), - parameters={"c": 0}, - ) - - with self.assertRaises(ValueError): - EllipticCurve( - self.secp128r1.curve.model, - self.secp128r1.curve.coordinate_model, - 15, - InfinityPoint(self.secp128r1.curve.coordinate_model), - parameters={"a": Mod(1, 5), "b": Mod(2, 5)}, - ) - - def test_to_coords(self): - affine = self.secp128r1.to_affine() - m1_coords = affine.curve.model.coordinates["projective-1"] - m3_coords = affine.curve.model.coordinates["projective-3"] - with self.assertRaises(UnsatisfiedAssumptionError): - affine.to_coords(m1_coords) - affine.to_coords(m3_coords) - - def test_to_affine(self): - affine = self.secp128r1.to_affine() - model = AffineCoordinateModel(affine.curve.model) - self.assertEqual(affine.curve.coordinate_model, model) - self.assertEqual(affine.generator.coordinate_model, model) - - def test_is_neutral(self): - self.assertTrue( - self.secp128r1.curve.is_neutral( - InfinityPoint(self.secp128r1.curve.coordinate_model) - ) +def test_init(secp128r1): + with pytest.raises(ValueError): + EllipticCurve( + MontgomeryModel(), + secp128r1.curve.coordinate_model, + 1, + InfinityPoint(secp128r1.curve.coordinate_model), + parameters={}, ) - def test_is_on_curve(self): - self.assertTrue(self.secp128r1.curve.is_on_curve(self.secp128r1.curve.neutral)) - pt = Point( - self.secp128r1.curve.coordinate_model, - X=Mod(0x161FF7528B899B2D0C28607CA52C5B86, self.secp128r1.curve.prime), - Y=Mod(0xCF5AC8395BAFEB13C02DA292DDED7A83, self.secp128r1.curve.prime), - Z=Mod(1, self.secp128r1.curve.prime), + with pytest.raises(ValueError): + EllipticCurve( + secp128r1.curve.model, + secp128r1.curve.coordinate_model, + 15, + InfinityPoint(secp128r1.curve.coordinate_model), + parameters={"c": 0}, ) - self.assertTrue(self.secp128r1.curve.is_on_curve(pt)) - self.assertTrue(self.secp128r1.curve.is_on_curve(pt.to_affine())) - other = Point( - self.secp128r1.curve.coordinate_model, - X=Mod(0x161FF7528B899B2D0C28607CA52C5B86, self.secp128r1.curve.prime), - Y=Mod(0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, self.secp128r1.curve.prime), - Z=Mod(1, self.secp128r1.curve.prime), - ) - self.assertFalse(self.secp128r1.curve.is_on_curve(other)) - self.assertFalse(self.secp128r1.curve.is_on_curve(self.curve25519.generator)) - - def test_affine_add(self): - pt = Point( - AffineCoordinateModel(self.secp128r1.curve.model), - x=Mod(0xEB916224EDA4FB356421773573297C15, self.secp128r1.curve.prime), - y=Mod(0xBCDAF32A2C08FD4271228FEF35070848, self.secp128r1.curve.prime), - ) - self.assertIsNotNone(self.secp128r1.curve.affine_add(self.affine_base, pt)) - added = self.secp128r1.curve.affine_add(self.affine_base, self.affine_base) - doubled = self.secp128r1.curve.affine_double(self.affine_base) - self.assertEqual(added, doubled) - self.assertEqual( - self.secp128r1.curve.affine_add(self.secp128r1.curve.neutral, pt), pt - ) - self.assertEqual( - self.secp128r1.curve.affine_add(pt, self.secp128r1.curve.neutral), pt + with pytest.raises(ValueError): + EllipticCurve( + secp128r1.curve.model, + secp128r1.curve.coordinate_model, + 15, + InfinityPoint(secp128r1.curve.coordinate_model), + parameters={"a": Mod(1, 5), "b": Mod(2, 5)}, ) - def test_affine_double(self): - self.assertIsNotNone(self.secp128r1.curve.affine_double(self.affine_base)) - self.assertEqual( - self.secp128r1.curve.affine_double(self.secp128r1.curve.neutral), - self.secp128r1.curve.neutral, - ) - def test_affine_negate(self): - self.assertIsNotNone(self.secp128r1.curve.affine_negate(self.affine_base)) - self.assertEqual( - self.secp128r1.curve.affine_negate(self.secp128r1.curve.neutral), - self.secp128r1.curve.neutral, - ) - with self.assertRaises(ValueError): - self.secp128r1.curve.affine_negate(self.base) - with self.assertRaises(ValueError): - self.secp128r1.curve.affine_negate(self.curve25519.generator) - - def test_affine_multiply(self): - expected = self.affine_base - expected = self.secp128r1.curve.affine_double(expected) - expected = self.secp128r1.curve.affine_double(expected) - expected = self.secp128r1.curve.affine_add(expected, self.affine_base) - expected = self.secp128r1.curve.affine_double(expected) - self.assertEqual( - self.secp128r1.curve.affine_multiply(self.affine_base, 10), expected - ) - self.assertEqual( - self.secp128r1.curve.affine_multiply(self.secp128r1.curve.neutral, 10), - self.secp128r1.curve.neutral, +def test_to_coords(secp128r1): + affine = secp128r1.to_affine() + m1_coords = affine.curve.model.coordinates["projective-1"] + m3_coords = affine.curve.model.coordinates["projective-3"] + with pytest.raises(UnsatisfiedAssumptionError): + affine.to_coords(m1_coords) + affine.to_coords(m3_coords) + + +def test_to_affine(secp128r1): + affine = secp128r1.to_affine() + model = AffineCoordinateModel(affine.curve.model) + assert affine.curve.coordinate_model == model + assert affine.generator.coordinate_model == model + + +def test_is_neutral(secp128r1): + assert secp128r1.curve.is_neutral( + InfinityPoint(secp128r1.curve.coordinate_model) + ) + + +def test_is_on_curve(secp128r1, curve25519): + assert secp128r1.curve.is_on_curve(secp128r1.curve.neutral) + pt = Point( + secp128r1.curve.coordinate_model, + X=Mod(0x161FF7528B899B2D0C28607CA52C5B86, secp128r1.curve.prime), + Y=Mod(0xCF5AC8395BAFEB13C02DA292DDED7A83, secp128r1.curve.prime), + Z=Mod(1, secp128r1.curve.prime), + ) + assert secp128r1.curve.is_on_curve(pt) + assert secp128r1.curve.is_on_curve(pt.to_affine()) + other = Point( + secp128r1.curve.coordinate_model, + X=Mod(0x161FF7528B899B2D0C28607CA52C5B86, secp128r1.curve.prime), + Y=Mod(0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, secp128r1.curve.prime), + Z=Mod(1, secp128r1.curve.prime), + ) + assert not secp128r1.curve.is_on_curve(other) + assert not secp128r1.curve.is_on_curve(curve25519.generator) + + +def test_affine_add(secp128r1): + pt = Point( + AffineCoordinateModel(secp128r1.curve.model), + x=Mod(0xEB916224EDA4FB356421773573297C15, secp128r1.curve.prime), + y=Mod(0xBCDAF32A2C08FD4271228FEF35070848, secp128r1.curve.prime), + ) + affine_base = secp128r1.generator.to_affine() + assert secp128r1.curve.affine_add(affine_base, pt) is not None + + added = secp128r1.curve.affine_add(affine_base, affine_base) + doubled = secp128r1.curve.affine_double(affine_base) + assert added == doubled + assert secp128r1.curve.affine_add(secp128r1.curve.neutral, pt) == pt + assert secp128r1.curve.affine_add(pt, secp128r1.curve.neutral) == pt + + +def test_affine_double(secp128r1): + affine_base = secp128r1.generator.to_affine() + assert secp128r1.curve.affine_double(affine_base) is not None + assert secp128r1.curve.affine_double(secp128r1.curve.neutral) == \ + secp128r1.curve.neutral + + +def test_affine_negate(secp128r1, curve25519): + affine_base = secp128r1.generator.to_affine() + assert secp128r1.curve.affine_negate(affine_base) is not None + assert secp128r1.curve.affine_negate(secp128r1.curve.neutral) == \ + secp128r1.curve.neutral + with pytest.raises(ValueError): + secp128r1.curve.affine_negate(secp128r1.generator) + with pytest.raises(ValueError): + secp128r1.curve.affine_negate(curve25519.generator) + + +def test_affine_multiply(secp128r1, curve25519): + affine_base = secp128r1.generator.to_affine() + expected = affine_base + expected = secp128r1.curve.affine_double(expected) + expected = secp128r1.curve.affine_double(expected) + expected = secp128r1.curve.affine_add(expected, affine_base) + expected = secp128r1.curve.affine_double(expected) + assert secp128r1.curve.affine_multiply(affine_base, 10) == expected + assert secp128r1.curve.affine_multiply(secp128r1.curve.neutral, 10) == \ + secp128r1.curve.neutral + with pytest.raises(ValueError): + secp128r1.curve.affine_multiply(secp128r1.generator, 10) + with pytest.raises(ValueError): + secp128r1.curve.affine_multiply(curve25519.generator, 10) + + +def test_affine_neutral(secp128r1, curve25519, ed25519): + assert secp128r1.curve.affine_neutral is None + assert curve25519.curve.affine_neutral is None + assert ed25519.curve.affine_neutral is not None + + +def test_neutral_is_affine(secp128r1, curve25519, ed25519): + assert not secp128r1.curve.neutral_is_affine + assert not curve25519.curve.neutral_is_affine + assert ed25519.curve.neutral_is_affine + + +@pytest.mark.parametrize("curve_name", ["secp128r1", "curve25519", "ed25519"]) +def test_affine_random(curve_name, request): + params = request.getfixturevalue(curve_name) + for _ in range(20): + pt = params.curve.affine_random() + assert pt is not None + assert params.curve.is_on_curve(pt) + + +def test_eq(secp128r1, curve25519): + assert secp128r1.curve == secp128r1.curve + assert secp128r1.curve != curve25519.curve + assert secp128r1.curve is not None + + +def test_decode(secp128r1): + affine_curve = secp128r1.curve.to_affine() + affine_point = secp128r1.generator.to_affine() + decoded = affine_curve.decode_point(bytes(affine_point)) + assert decoded == affine_point + + affine_compressed_bytes = unhexlify("03161ff7528b899b2d0c28607ca52c5b86") + decoded_compressed = affine_curve.decode_point(affine_compressed_bytes) + assert decoded_compressed == affine_point + affine_compressed_bytes = unhexlify("02161ff7528b899b2d0c28607ca52c5b86") + decoded_compressed = affine_curve.decode_point(affine_compressed_bytes) + decoded_compressed = secp128r1.curve.affine_negate(decoded_compressed) + assert decoded_compressed == affine_point + + infinity_bytes = unhexlify("00") + decoded_infinity = affine_curve.decode_point(infinity_bytes) + assert affine_curve.neutral == decoded_infinity + + with pytest.raises(ValueError): + affine_curve.decode_point(unhexlify("03161ff7528b899b2d0c28607ca52c5b")) + with pytest.raises(ValueError): + affine_curve.decode_point( + unhexlify("04161ff7528b899b2d0c28607ca52c5b2c5b2c5b2c5b") ) - with self.assertRaises(ValueError): - self.secp128r1.curve.affine_multiply(self.base, 10) - with self.assertRaises(ValueError): - self.secp128r1.curve.affine_multiply(self.curve25519.generator, 10) - - def test_affine_neutral(self): - self.assertIsNone(self.secp128r1.curve.affine_neutral) - self.assertIsNone(self.curve25519.curve.affine_neutral) - self.assertIsNotNone(self.ed25519.curve.affine_neutral) - - def test_affine_random(self): - for params in [self.secp128r1, self.curve25519, self.ed25519]: - for _ in range(20): - pt = params.curve.affine_random() - self.assertIsNotNone(pt) - self.assertTrue(params.curve.is_on_curve(pt)) - - def test_neutral_is_affine(self): - self.assertFalse(self.secp128r1.curve.neutral_is_affine) - self.assertFalse(self.curve25519.curve.neutral_is_affine) - self.assertTrue(self.ed25519.curve.neutral_is_affine) - - def test_eq(self): - self.assertEqual(self.secp128r1.curve, self.secp128r1.curve) - self.assertNotEqual(self.secp128r1.curve, self.curve25519.curve) - self.assertNotEqual(self.secp128r1.curve, None) - - def test_decode(self): - affine_curve = self.secp128r1.curve.to_affine() - affine_point = self.secp128r1.generator.to_affine() - decoded = affine_curve.decode_point(bytes(affine_point)) - self.assertEqual(decoded, affine_point) - - affine_compressed_bytes = unhexlify("03161ff7528b899b2d0c28607ca52c5b86") - decoded_compressed = affine_curve.decode_point(affine_compressed_bytes) - self.assertEqual(decoded_compressed, affine_point) - affine_compressed_bytes = unhexlify("02161ff7528b899b2d0c28607ca52c5b86") - decoded_compressed = affine_curve.decode_point(affine_compressed_bytes) - decoded_compressed = self.secp128r1.curve.affine_negate(decoded_compressed) - self.assertEqual(decoded_compressed, affine_point) - - infinity_bytes = unhexlify("00") - decoded_infinity = affine_curve.decode_point(infinity_bytes) - self.assertEqual(affine_curve.neutral, decoded_infinity) - - with self.assertRaises(ValueError): - affine_curve.decode_point(unhexlify("03161ff7528b899b2d0c28607ca52c5b")) - with self.assertRaises(ValueError): - affine_curve.decode_point( - unhexlify("04161ff7528b899b2d0c28607ca52c5b2c5b2c5b2c5b") - ) - with self.assertRaises(ValueError): - affine_curve.decode_point(unhexlify("7a161ff7528b899b2d0c28607ca52c5b86")) - with self.assertRaises(ValueError): - affine_curve.decode_point(unhexlify("03161ff7528b899b2d0c28607ca52c5b88")) + with pytest.raises(ValueError): + affine_curve.decode_point(unhexlify("7a161ff7528b899b2d0c28607ca52c5b86")) + with pytest.raises(ValueError): + affine_curve.decode_point(unhexlify("03161ff7528b899b2d0c28607ca52c5b88")) diff --git a/test/ec/test_divpoly.py b/test/ec/test_divpoly.py index f09fdf0..aace00b 100644 --- a/test/ec/test_divpoly.py +++ b/test/ec/test_divpoly.py @@ -1,188 +1,112 @@ -from unittest import TestCase import json from importlib.resources import files import test.data.divpoly from sympy import FF from pyecsca.ec.divpoly import a_invariants, b_invariants, divpoly0, divpoly, mult_by_n -from pyecsca.ec.model import ShortWeierstrassModel -from pyecsca.ec.params import get_params -class DivpolyTests(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_ainvs(secp128r1): + ainvs = a_invariants(secp128r1.curve) + assert ainvs == (0, 0, 0, 340282366762482138434845932244680310780, 308990863222245658030922601041482374867) - def test_ainvs(self): - ainvs = a_invariants(self.secp128r1.curve) - self.assertSequenceEqual(ainvs, (0, - 0, - 0, - 340282366762482138434845932244680310780, - 308990863222245658030922601041482374867)) - def test_binvs(self): - binvs = b_invariants(self.secp128r1.curve) - self.assertSequenceEqual(binvs, (0, - 340282366762482138434845932244680310777, - 215116352601536216819152607431888567119, - 340282366762482138434845932244680310774)) +def test_binvs(secp128r1): + binvs = b_invariants(secp128r1.curve) + assert binvs == (0, 340282366762482138434845932244680310777, 215116352601536216819152607431888567119, + 340282366762482138434845932244680310774) - def test_divpoly0(self): - # Data from sagemath - coeffs = [11, - 0, - 340282366762482138434845932244680302401, - 211962053797180672439257756222135086642, - 340282366762482138434845932244678441564, - 115415922367823003571854983213102698477, - 152803211743444076787231275062278784385, - 68540219804769369063918923691867278088, - 43207172520353703997069627419519708522, - 83208285732019037267730920881743782729, - 93286967763556583502947234289842152563, - 324950611928652823046744874201355360259, - 244242343224213805514200367379671854852, - 307096814154284337284845014037169929735, - 180946781765592277412990188457219828893, - 301253861469456022084288029442105687698, - 58053323975526190296189278379252064657, - 224437885189054146208302696540070489578, - 281987318191429654256483850017931541622, - 21449216018131966691124843738286677726, - 10958264881628724646042625283328121348, - 104868338562600481545003572552335444641, - 127205813185570107009206143413997395181, - 116865717360861207318274706645935808417, - 281460458922812844939222119784601506753, - 336607098463310980140968249747513775735, - 304486486784143285234063826161805094682, - 194935097339732797131694429642153881938, - 193523171473792085604518744912658246509, - 204844449336357293979832621297234119270, - 244481753281744913785581086721299830802, - 46816299473081369405217767361380254657, - 303070923752707405164354702252828590781, - 222516549119176621389776816552836322766, - 292006660232236762950883960515487362063, - 53617127992846936725441702182362940200, - 242498306026562585655027965022211017540, - 25039963304689451659955607939868533124, - 328580435950647191774558154445103295305, - 24226614081978788956695324769468902511, - 147945052666123617872720080832548744564, - 287190187011075399698210761813202261601, - 117131681517270554750959286838283723521, - 35018410385280384289320020556813474742, - 83939964512240352730304831725346032711, - 147219996946006689656600631222993527180, - 280430477096741745234510250577626566690, - 32753113267385981127807026368593329576, - 105134319561523011785486683031223863934, - 206456116679151691099661865534540095270, - 116180470443213022739312068090342951131, - 245850120846480965440408943459023315919, - 45805943896736805301879725516256422457, - 226777421435695229777151315574975350291, - 283680841707610526659029980964566557627, - 53168487339451866167506032177471934158, - 69212302225932892622760219621519562036, - 183916411340675637978873336955593385541, - 119478537598919956688656337369481692789, - 234767298887335988751880131162396819780, - 218412162101425422347176804186940045781] - K = FF(self.secp128r1.curve.prime) - poly = divpoly0(self.secp128r1.curve, 11)[11] - computed = list(map(K, poly.all_coeffs())) - self.assertListEqual(coeffs, computed) +def test_divpoly0(secp128r1): + # Data from sagemath + coeffs = [11, 0, 340282366762482138434845932244680302401, 211962053797180672439257756222135086642, + 340282366762482138434845932244678441564, 115415922367823003571854983213102698477, + 152803211743444076787231275062278784385, 68540219804769369063918923691867278088, + 43207172520353703997069627419519708522, 83208285732019037267730920881743782729, + 93286967763556583502947234289842152563, 324950611928652823046744874201355360259, + 244242343224213805514200367379671854852, 307096814154284337284845014037169929735, + 180946781765592277412990188457219828893, 301253861469456022084288029442105687698, + 58053323975526190296189278379252064657, 224437885189054146208302696540070489578, + 281987318191429654256483850017931541622, 21449216018131966691124843738286677726, + 10958264881628724646042625283328121348, 104868338562600481545003572552335444641, + 127205813185570107009206143413997395181, 116865717360861207318274706645935808417, + 281460458922812844939222119784601506753, 336607098463310980140968249747513775735, + 304486486784143285234063826161805094682, 194935097339732797131694429642153881938, + 193523171473792085604518744912658246509, 204844449336357293979832621297234119270, + 244481753281744913785581086721299830802, 46816299473081369405217767361380254657, + 303070923752707405164354702252828590781, 222516549119176621389776816552836322766, + 292006660232236762950883960515487362063, 53617127992846936725441702182362940200, + 242498306026562585655027965022211017540, 25039963304689451659955607939868533124, + 328580435950647191774558154445103295305, 24226614081978788956695324769468902511, + 147945052666123617872720080832548744564, 287190187011075399698210761813202261601, + 117131681517270554750959286838283723521, 35018410385280384289320020556813474742, + 83939964512240352730304831725346032711, 147219996946006689656600631222993527180, + 280430477096741745234510250577626566690, 32753113267385981127807026368593329576, + 105134319561523011785486683031223863934, 206456116679151691099661865534540095270, + 116180470443213022739312068090342951131, 245850120846480965440408943459023315919, + 45805943896736805301879725516256422457, 226777421435695229777151315574975350291, + 283680841707610526659029980964566557627, 53168487339451866167506032177471934158, + 69212302225932892622760219621519562036, 183916411340675637978873336955593385541, + 119478537598919956688656337369481692789, 234767298887335988751880131162396819780, + 218412162101425422347176804186940045781] + K = FF(secp128r1.curve.prime) + poly = divpoly0(secp128r1.curve, 11)[11] + computed = list(map(K, poly.all_coeffs())) + assert coeffs == computed - def test_divpoly(self): - # Data from sagemath - K = FF(self.secp128r1.curve.prime) - coeffs_0 = { - (0,): K(16020440675387382717114730680672549016), - (1,): K(269851015321770885610377847857290470365), - (2,): K(340282366762482138434845932244680310693), - (3,): K(109469325440469337582450480850803806492), - (4,): K(340282366762482138434845932244680310753), - (6,): K(2) - } - self.assertDictEqual(divpoly(self.secp128r1.curve, 4, 0).as_dict(), coeffs_0) - coeffs_1 = { - (6, 1): K(4), - (4, 1): K(340282366762482138434845932244680310723), - (3, 1): K(218938650880938675164900961701607612984), - (2, 1): K(340282366762482138434845932244680310603), - (1, 1): K(199419663881059632785909763469900629947), - (0, 1): K(32040881350774765434229461361345098032) - } - self.assertDictEqual(divpoly(self.secp128r1.curve, 4, 1).as_dict(), coeffs_1) - coeffs_2 = { - (9,): K(8), - (7,): K(340282366762482138434845932244680310639), - (6,): K(187545273439985507098415273777631738640), - (4,): K(117928913205007755574446043156465405646), - (3,): K(244159722710157842132157548160645018307), - (2,): K(200234655086793134086408617236124137371), - (1,): K(51914434605509249526780779992574428819), - (0,): K(60581150995923875019702403440670701629) - } - self.assertDictEqual(divpoly(self.secp128r1.curve, 4, 2).as_dict(), coeffs_2) - def test_mult_by_n(self): - # Data from sagemath - K = FF(self.secp128r1.curve.prime) - coeffs_mx_num = [1, - 0, - 6, - 250332028321891843231386649625583487328, - 9] - coeffs_mx_denom = [4, - 0, - 340282366762482138434845932244680310771, - 215116352601536216819152607431888567119] - coeffs_my_num = { - (6, 1): K(8), - (4, 1): K(340282366762482138434845932244680310663), - (3, 1): K(97594934999395211894955991158534915185), - (2, 1): K(340282366762482138434845932244680310423), - (1, 1): K(58556960999637127136973594695120949111), - (0, 1): K(64081762701549530868458922722690196064) - } - coeffs_my_denom = { - (6, 0): K(64), - (4, 0): K(340282366762482138434845932244680310399), - (3, 0): K(78075947999516169515964792926827932148), - (2, 0): K(576), - (1, 0): K(106054522763933629886951553464196514339), - (0, 0): K(276200604060932607566387009521990114935) - } - mx, my = mult_by_n(self.secp128r1.curve, 2) - mx_num, mx_denom = mx - self.assertListEqual(coeffs_mx_num, list(map(K, mx_num.all_coeffs()))) - self.assertListEqual(coeffs_mx_denom, list(map(K, mx_denom.all_coeffs()))) - my_num, my_denom = my - self.assertDictEqual(my_num.as_dict(), coeffs_my_num) - self.assertDictEqual(my_denom.as_dict(), coeffs_my_denom) +def test_divpoly(secp128r1): + # Data from sagemath + K = FF(secp128r1.curve.prime) + coeffs_0 = {(0,): K(16020440675387382717114730680672549016), (1,): K(269851015321770885610377847857290470365), + (2,): K(340282366762482138434845932244680310693), (3,): K(109469325440469337582450480850803806492), + (4,): K(340282366762482138434845932244680310753), (6,): K(2)} + assert divpoly(secp128r1.curve, 4, 0).as_dict() == coeffs_0 + coeffs_1 = {(6, 1): K(4), (4, 1): K(340282366762482138434845932244680310723), + (3, 1): K(218938650880938675164900961701607612984), (2, 1): K(340282366762482138434845932244680310603), + (1, 1): K(199419663881059632785909763469900629947), (0, 1): K(32040881350774765434229461361345098032)} + assert divpoly(secp128r1.curve, 4, 1).as_dict() == coeffs_1 + coeffs_2 = {(9,): K(8), (7,): K(340282366762482138434845932244680310639), + (6,): K(187545273439985507098415273777631738640), (4,): K(117928913205007755574446043156465405646), + (3,): K(244159722710157842132157548160645018307), (2,): K(200234655086793134086408617236124137371), + (1,): K(51914434605509249526780779992574428819), (0,): K(60581150995923875019702403440670701629)} + assert divpoly(secp128r1.curve, 4, 2).as_dict() == coeffs_2 - def test_mult_by_n_large(self): - K = FF(self.secp128r1.curve.prime) - mx, my = mult_by_n(self.secp128r1.curve, 21) - with files(test.data.divpoly).joinpath("mult_21.json").open("r") as f: - sage_data = json.load(f) - sage_data["mx"][0] = {eval(key): K(val) for key, val in sage_data["mx"][0].items()} - sage_data["mx"][1] = {eval(key): K(val) for key, val in sage_data["mx"][1].items()} - sage_data["my"][0] = {eval(key): K(val) for key, val in sage_data["my"][0].items()} - sage_data["my"][1] = {eval(key): K(val) for key, val in sage_data["my"][1].items()} - self.assertDictEqual(mx[0].as_dict(), sage_data["mx"][0]) - self.assertDictEqual(mx[1].as_dict(), sage_data["mx"][1]) +def test_mult_by_n(secp128r1): + # Data from sagemath + K = FF(secp128r1.curve.prime) + coeffs_mx_num = [1, 0, 6, 250332028321891843231386649625583487328, 9] + coeffs_mx_denom = [4, 0, 340282366762482138434845932244680310771, 215116352601536216819152607431888567119] + coeffs_my_num = {(6, 1): K(8), (4, 1): K(340282366762482138434845932244680310663), + (3, 1): K(97594934999395211894955991158534915185), + (2, 1): K(340282366762482138434845932244680310423), + (1, 1): K(58556960999637127136973594695120949111), + (0, 1): K(64081762701549530868458922722690196064)} + coeffs_my_denom = {(6, 0): K(64), (4, 0): K(340282366762482138434845932244680310399), + (3, 0): K(78075947999516169515964792926827932148), (2, 0): K(576), + (1, 0): K(106054522763933629886951553464196514339), + (0, 0): K(276200604060932607566387009521990114935)} + mx, my = mult_by_n(secp128r1.curve, 2) + mx_num, mx_denom = mx + assert coeffs_mx_num == list(map(K, mx_num.all_coeffs())) + assert coeffs_mx_denom == list(map(K, mx_denom.all_coeffs())) + my_num, my_denom = my + assert my_num.as_dict() == coeffs_my_num + assert my_denom.as_dict() == coeffs_my_denom - self.assertDictEqual(my[0].as_dict(), sage_data["my"][0]) - self.assertDictEqual(my[1].as_dict(), sage_data["my"][1]) + +def test_mult_by_n_large(secp128r1): + K = FF(secp128r1.curve.prime) + mx, my = mult_by_n(secp128r1.curve, 21) + with files(test.data.divpoly).joinpath("mult_21.json").open("r") as f: + sage_data = json.load(f) + sage_data["mx"][0] = {eval(key): K(val) for key, val in sage_data["mx"][0].items()} + sage_data["mx"][1] = {eval(key): K(val) for key, val in sage_data["mx"][1].items()} + sage_data["my"][0] = {eval(key): K(val) for key, val in sage_data["my"][0].items()} + sage_data["my"][1] = {eval(key): K(val) for key, val in sage_data["my"][1].items()} + + assert mx[0].as_dict() == sage_data["mx"][0] + assert mx[1].as_dict() == sage_data["mx"][1] + assert my[0].as_dict() == sage_data["my"][0] + assert my[1].as_dict() == sage_data["my"][1] diff --git a/test/ec/test_formula.py b/test/ec/test_formula.py index ffae4c4..420e0b3 100644 --- a/test/ec/test_formula.py +++ b/test/ec/test_formula.py @@ -1,5 +1,4 @@ -from unittest import TestCase - +import pytest from sympy import FF, symbols from pyecsca.ec.mod import SymbolicMod, Mod @@ -9,99 +8,115 @@ from pyecsca.ec.params import get_params from pyecsca.ec.point import Point -class FormulaTests(TestCase): - def setUp(self): - self.secp128r1 = get_params("secg", "secp128r1", "projective") - self.add = self.secp128r1.curve.coordinate_model.formulas["add-2007-bl"] - self.dbl = self.secp128r1.curve.coordinate_model.formulas["dbl-2007-bl"] - self.mdbl = self.secp128r1.curve.coordinate_model.formulas["mdbl-2007-bl"] - self.jac_secp128r1 = get_params("secg", "secp128r1", "jacobian") - self.jac_dbl = self.jac_secp128r1.curve.coordinate_model.formulas[ - "dbl-1998-hnm" - ] - - def test_wrong_call(self): - with self.assertRaises(ValueError): - self.add(self.secp128r1.curve.prime) - with self.assertRaises(ValueError): - self.add( - self.secp128r1.curve.prime, - self.secp128r1.generator.to_affine(), - self.secp128r1.generator.to_affine(), - ) +@pytest.fixture() +def add(secp128r1): + return secp128r1.curve.coordinate_model.formulas["add-2007-bl"] + + +@pytest.fixture() +def dbl(secp128r1): + return secp128r1.curve.coordinate_model.formulas["dbl-2007-bl"] + + +@pytest.fixture() +def mdbl(secp128r1): + return secp128r1.curve.coordinate_model.formulas["mdbl-2007-bl"] + - def test_indices(self): - self.assertEqual(self.add.input_index, 1) - self.assertEqual(self.add.output_index, 3) - - def test_inputs_outputs(self): - self.assertEqual(self.add.inputs, {"X1", "Y1", "Z1", "X2", "Y2", "Z2"}) - self.assertEqual(self.add.outputs, {"X3", "Y3", "Z3"}) - - def test_eq(self): - self.assertEqual(self.add, self.add) - self.assertNotEqual(self.add, self.dbl) - - def test_num_ops(self): - self.assertEqual(self.add.num_operations, 33) - self.assertEqual(self.add.num_multiplications, 17) - self.assertEqual(self.add.num_divisions, 0) - self.assertEqual(self.add.num_inversions, 0) - self.assertEqual(self.add.num_powers, 0) - self.assertEqual(self.add.num_squarings, 6) - self.assertEqual(self.add.num_addsubs, 10) - - def test_assumptions(self): - res = self.mdbl( - self.secp128r1.curve.prime, - self.secp128r1.generator, - **self.secp128r1.curve.parameters +def test_wrong_call(secp128r1, add): + with pytest.raises(ValueError): + add(secp128r1.curve.prime) + with pytest.raises(ValueError): + add( + secp128r1.curve.prime, + secp128r1.generator.to_affine(), + secp128r1.generator.to_affine(), ) - self.assertIsNotNone(res) - - coords = { - name: value * 5 for name, value in self.secp128r1.generator.coords.items() - } - other = Point(self.secp128r1.generator.coordinate_model, **coords) - with self.assertRaises(UnsatisfiedAssumptionError): - self.mdbl( - self.secp128r1.curve.prime, other, **self.secp128r1.curve.parameters - ) - with TemporaryConfig() as cfg: - cfg.ec.unsatisfied_formula_assumption_action = "ignore" - pt = self.mdbl( - self.secp128r1.curve.prime, other, **self.secp128r1.curve.parameters - ) - self.assertIsNotNone(pt) - def test_parameters(self): - res = self.jac_dbl( - self.secp128r1.curve.prime, - self.jac_secp128r1.generator, - **self.jac_secp128r1.curve.parameters + +def test_indices(add): + assert add.input_index == 1 + assert add.output_index == 3 + + +def test_inputs_outputs(add): + assert add.inputs == {"X1", "Y1", "Z1", "X2", "Y2", "Z2"} + assert add.outputs == {"X3", "Y3", "Z3"} + + +def test_eq(add, dbl): + assert add == add + assert add != dbl + + +def test_num_ops(add): + assert add.num_operations == 33 + assert add.num_multiplications == 17 + assert add.num_divisions == 0 + assert add.num_inversions == 0 + assert add.num_powers == 0 + assert add.num_squarings == 6 + assert add.num_addsubs == 10 + + +def test_assumptions(secp128r1, mdbl): + res = mdbl( + secp128r1.curve.prime, + secp128r1.generator, + **secp128r1.curve.parameters + ) + assert res is not None + + coords = { + name: value * 5 for name, value in secp128r1.generator.coords.items() + } + other = Point(secp128r1.generator.coordinate_model, **coords) + with pytest.raises(UnsatisfiedAssumptionError): + mdbl( + secp128r1.curve.prime, other, **secp128r1.curve.parameters ) - self.assertIsNotNone(res) - - def test_symbolic(self): - p = self.secp128r1.curve.prime - k = FF(p) - coords = self.secp128r1.curve.coordinate_model - sympy_params = { - key: SymbolicMod(k(int(value)), p) - for key, value in self.secp128r1.curve.parameters.items() - } - symbolic_point = Point( - coords, **{key: SymbolicMod(symbols(key), p) for key in coords.variables} + with TemporaryConfig() as cfg: + cfg.ec.unsatisfied_formula_assumption_action = "ignore" + pt = mdbl( + secp128r1.curve.prime, other, **secp128r1.curve.parameters ) - symbolic_double = self.dbl(p, symbolic_point, **sympy_params)[0] - generator_double = self.dbl( - p, self.secp128r1.generator, **self.secp128r1.curve.parameters - )[0] - for outer_var in coords.variables: - symbolic_val = getattr(symbolic_double, outer_var).x - generator_val = getattr(generator_double, outer_var).x - for inner_var in coords.variables: - symbolic_val = symbolic_val.subs( - inner_var, k(getattr(self.secp128r1.generator, inner_var).x) - ) - self.assertEqual(Mod(int(symbolic_val), p), Mod(generator_val, p)) + assert pt is not None + + +def test_parameters(): + jac_secp128r1 = get_params("secg", "secp128r1", "jacobian") + jac_dbl = jac_secp128r1.curve.coordinate_model.formulas[ + "dbl-1998-hnm" + ] + + res = jac_dbl( + jac_secp128r1.curve.prime, + jac_secp128r1.generator, + **jac_secp128r1.curve.parameters + ) + assert res is not None + + +def test_symbolic(secp128r1, dbl): + p = secp128r1.curve.prime + k = FF(p) + coords = secp128r1.curve.coordinate_model + sympy_params = { + key: SymbolicMod(k(int(value)), p) + for key, value in secp128r1.curve.parameters.items() + } + symbolic_point = Point( + coords, **{key: SymbolicMod(symbols(key), p) for key in coords.variables} + ) + symbolic_double = dbl(p, symbolic_point, **sympy_params)[0] + generator_double = dbl( + p, secp128r1.generator, **secp128r1.curve.parameters + )[0] + for outer_var in coords.variables: + symbolic_val = getattr(symbolic_double, outer_var).x + generator_val = getattr(generator_double, outer_var).x + for inner_var in coords.variables: + symbolic_val = symbolic_val.subs( + inner_var, k(getattr(secp128r1.generator, inner_var).x) + ) + assert Mod(int(symbolic_val), p) == Mod(generator_val, p) diff --git a/test/ec/test_key_agreement.py b/test/ec/test_key_agreement.py index 240c174..14bd138 100644 --- a/test/ec/test_key_agreement.py +++ b/test/ec/test_key_agreement.py @@ -1,8 +1,4 @@ -from unittest import TestCase - -from parameterized import parameterized - -from pyecsca.ec.params import get_params +import pytest from pyecsca.ec.key_agreement import ( ECDH_NONE, ECDH_SHA1, @@ -15,31 +11,33 @@ from pyecsca.ec.mod import Mod from pyecsca.ec.mult import LTRMultiplier -class KeyAgreementTests(TestCase): - def setUp(self): - self.secp128r1 = get_params("secg", "secp128r1", "projective") - self.add = self.secp128r1.curve.coordinate_model.formulas["add-2007-bl"] - self.dbl = self.secp128r1.curve.coordinate_model.formulas["dbl-2007-bl"] - self.mult = LTRMultiplier(self.add, self.dbl) - self.priv_a = Mod(0xDEADBEEF, self.secp128r1.order) - self.mult.init(self.secp128r1, self.secp128r1.generator) - self.pub_a = self.mult.multiply(int(self.priv_a)) - self.priv_b = Mod(0xCAFEBABE, self.secp128r1.order) - self.pub_b = self.mult.multiply(int(self.priv_b)) - - @parameterized.expand( - [ - ("NONE", ECDH_NONE), - ("SHA1", ECDH_SHA1), - ("SHA224", ECDH_SHA224), - ("SHA256", ECDH_SHA256), - ("SHA384", ECDH_SHA384), - ("SHA512", ECDH_SHA512), - ] - ) - def test_all(self, name, algo): - result_ab = algo(self.mult, self.secp128r1, self.pub_a, self.priv_b).perform() - result_ba = algo(self.mult, self.secp128r1, self.pub_b, self.priv_a).perform() - self.assertEqual(result_ab, result_ba) - - # TODO: Add KAT-based tests here. +@pytest.fixture() +def mult(secp128r1): + add = secp128r1.curve.coordinate_model.formulas["add-2007-bl"] + dbl = secp128r1.curve.coordinate_model.formulas["dbl-2007-bl"] + return LTRMultiplier(add, dbl) + + +@pytest.fixture() +def keypair_a(secp128r1, mult): + priv_a = Mod(0xDEADBEEF, secp128r1.order) + mult.init(secp128r1, secp128r1.generator) + pub_a = mult.multiply(int(priv_a)) + return priv_a, pub_a + + +@pytest.fixture() +def keypair_b(secp128r1, mult): + priv_b = Mod(0xCAFEBABE, secp128r1.order) + mult.init(secp128r1, secp128r1.generator) + pub_b = mult.multiply(int(priv_b)) + return priv_b, pub_b + + +@pytest.mark.parametrize("algo", [ECDH_NONE, ECDH_SHA1, ECDH_SHA224, ECDH_SHA256, ECDH_SHA384, ECDH_SHA512]) +def test_ka(algo, mult, secp128r1, keypair_a, keypair_b): + result_ab = algo(mult, secp128r1, keypair_a[1], keypair_b[0]).perform() + result_ba = algo(mult, secp128r1, keypair_b[1], keypair_a[0]).perform() + assert result_ab == result_ba + +# TODO: Add KAT-based tests here. diff --git a/test/ec/test_key_generation.py b/test/ec/test_key_generation.py index 7eb26f0..512aaac 100644 --- a/test/ec/test_key_generation.py +++ b/test/ec/test_key_generation.py @@ -1,25 +1,24 @@ -from unittest import TestCase +import pytest -from pyecsca.ec.params import get_params from pyecsca.ec.key_generation import KeyGeneration from pyecsca.ec.mult import LTRMultiplier -class KeyGenerationTests(TestCase): - def setUp(self): - self.secp128r1 = get_params("secg", "secp128r1", "projective") - self.add = self.secp128r1.curve.coordinate_model.formulas["add-2007-bl"] - self.dbl = self.secp128r1.curve.coordinate_model.formulas["dbl-2007-bl"] - self.mult = LTRMultiplier(self.add, self.dbl) +@pytest.fixture() +def mult(secp128r1): + add = secp128r1.curve.coordinate_model.formulas["add-2007-bl"] + dbl = secp128r1.curve.coordinate_model.formulas["dbl-2007-bl"] + return LTRMultiplier(add, dbl) - def test_basic(self): - generator = KeyGeneration(self.mult, self.secp128r1) - priv, pub = generator.generate() - self.assertIsNotNone(priv) - self.assertIsNotNone(pub) - self.assertTrue(self.secp128r1.curve.is_on_curve(pub)) - generator = KeyGeneration(self.mult, self.secp128r1, True) - priv, pub = generator.generate() - self.assertIsNotNone(priv) - self.assertIsNotNone(pub) - self.assertTrue(self.secp128r1.curve.is_on_curve(pub)) + +def test_basic(secp128r1, mult): + generator = KeyGeneration(mult, secp128r1) + priv, pub = generator.generate() + assert priv is not None + assert pub is not None + assert secp128r1.curve.is_on_curve(pub) + generator = KeyGeneration(mult, secp128r1, True) + priv, pub = generator.generate() + assert priv is not None + assert pub is not None + assert secp128r1.curve.is_on_curve(pub) diff --git a/test/ec/test_mod.py b/test/ec/test_mod.py index 62022b0..29b818e 100644 --- a/test/ec/test_mod.py +++ b/test/ec/test_mod.py @@ -1,6 +1,7 @@ import warnings + +import pytest from sympy import FF, symbols -from unittest import TestCase from pyecsca.ec.mod import ( Mod, @@ -22,153 +23,149 @@ from pyecsca.ec.error import ( from pyecsca.misc.cfg import getconfig, TemporaryConfig -class ModTests(TestCase): - def test_gcd(self): - self.assertEqual(gcd(15, 20), 5) - self.assertEqual(extgcd(15, 0), (1, 0, 15)) - self.assertEqual(extgcd(15, 20), (-1, 1, 5)) - - def test_jacobi(self): - self.assertEqual(jacobi(5, 1153486465415345646578465454655646543248656451), 1) - self.assertEqual( - jacobi(564786456646845, 46874698564153465453246546545456849797895547657), -1 - ) - self.assertEqual( - jacobi(564786456646845, 46874698564153465453246546545456849797895), 0 - ) - - def test_miller_rabin(self): - self.assertTrue(miller_rabin(2)) - self.assertTrue(miller_rabin(3)) - self.assertTrue(miller_rabin(5)) - self.assertFalse(miller_rabin(8)) - self.assertTrue( - miller_rabin(0xE807561107CCF8FA82AF74FD492543A918CA2E9C13750233A9) - ) - self.assertFalse( - miller_rabin(0x6F6889DEB08DA211927370810F026EB4C17B17755F72EA005) - ) - - def test_inverse(self): - p = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF - self.assertEqual( - Mod( - 0x702BDAFD3C1C837B23A1CB196ED7F9FADB333C5CFE4A462BE32ADCD67BFB6AC1, p - ).inverse(), - Mod(0x1CB2E5274BBA085C4CA88EEDE75AE77949E7A410C80368376E97AB22EB590F9D, p), - ) - with self.assertRaises(NonInvertibleError): - Mod(0, p).inverse() - with self.assertRaises(NonInvertibleError): - Mod(5, 10).inverse() - getconfig().ec.no_inverse_action = "warning" - with warnings.catch_warnings(record=True) as w: - Mod(0, p).inverse() - self.assertTrue(issubclass(w[0].category, NonInvertibleWarning)) - with warnings.catch_warnings(record=True) as w: - Mod(5, 10).inverse() - self.assertTrue(issubclass(w[0].category, NonInvertibleWarning)) - getconfig().ec.no_inverse_action = "ignore" - Mod(0, p).inverse() - Mod(5, 10).inverse() - getconfig().ec.no_inverse_action = "error" +def test_gcd(): + assert gcd(15, 20) == 5 + assert extgcd(15, 0) == (1, 0, 15) + assert extgcd(15, 20) == (-1, 1, 5) - def test_is_residue(self): - self.assertTrue(Mod(4, 11).is_residue()) - self.assertFalse(Mod(11, 31).is_residue()) - self.assertTrue(Mod(0, 7).is_residue()) - self.assertTrue(Mod(1, 2).is_residue()) - def test_sqrt(self): - p = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF - self.assertIn( - Mod( - 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC, p - ).sqrt(), - ( - 0x9ADD512515B70D9EC471151C1DEC46625CD18B37BDE7CA7FB2C8B31D7033599D, - 0x6522AED9EA48F2623B8EEAE3E213B99DA32E74C9421835804D374CE28FCCA662, - ), - ) - with self.assertRaises(NonResidueError): - Mod( - 0x702BDAFD3C1C837B23A1CB196ED7F9FADB333C5CFE4A462BE32ADCD67BFB6AC1, p - ).sqrt() - getconfig().ec.non_residue_action = "warning" - with warnings.catch_warnings(record=True) as w: - Mod( - 0x702BDAFD3C1C837B23A1CB196ED7F9FADB333C5CFE4A462BE32ADCD67BFB6AC1, p - ).sqrt() - self.assertTrue(issubclass(w[0].category, NonResidueWarning)) - getconfig().ec.non_residue_action = "ignore" +def test_jacobi(): + assert jacobi(5, 1153486465415345646578465454655646543248656451) == 1 + assert jacobi(564786456646845, 46874698564153465453246546545456849797895547657) == -1 + assert jacobi(564786456646845, 46874698564153465453246546545456849797895) == 0 + + +def test_miller_rabin(): + assert miller_rabin(2) + assert miller_rabin(3) + assert miller_rabin(5) + assert not miller_rabin(8) + assert miller_rabin(0xE807561107CCF8FA82AF74FD492543A918CA2E9C13750233A9) + assert not miller_rabin(0x6F6889DEB08DA211927370810F026EB4C17B17755F72EA005) + + +def test_inverse(): + p = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF + assert Mod( + 0x702BDAFD3C1C837B23A1CB196ED7F9FADB333C5CFE4A462BE32ADCD67BFB6AC1, p + ).inverse() == \ + Mod(0x1CB2E5274BBA085C4CA88EEDE75AE77949E7A410C80368376E97AB22EB590F9D, p) + with pytest.raises(NonInvertibleError): + Mod(0, p).inverse() + with pytest.raises(NonInvertibleError): + Mod(5, 10).inverse() + getconfig().ec.no_inverse_action = "warning" + with warnings.catch_warnings(record=True) as w: + Mod(0, p).inverse() + assert issubclass(w[0].category, NonInvertibleWarning) + with warnings.catch_warnings(record=True) as w: + Mod(5, 10).inverse() + assert issubclass(w[0].category, NonInvertibleWarning) + getconfig().ec.no_inverse_action = "ignore" + Mod(0, p).inverse() + Mod(5, 10).inverse() + getconfig().ec.no_inverse_action = "error" + + +def test_is_residue(): + assert Mod(4, 11).is_residue() + assert not Mod(11, 31).is_residue() + assert Mod(0, 7).is_residue() + assert Mod(1, 2).is_residue() + + +def test_sqrt(): + p = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF + assert Mod( + 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC, p + ).sqrt() in \ + ( + 0x9ADD512515B70D9EC471151C1DEC46625CD18B37BDE7CA7FB2C8B31D7033599D, + 0x6522AED9EA48F2623B8EEAE3E213B99DA32E74C9421835804D374CE28FCCA662, + ) + with pytest.raises(NonResidueError): Mod( 0x702BDAFD3C1C837B23A1CB196ED7F9FADB333C5CFE4A462BE32ADCD67BFB6AC1, p ).sqrt() - with TemporaryConfig() as cfg: - cfg.ec.non_residue_action = "warning" - with warnings.catch_warnings(record=True) as w: - Mod( - 0x702BDAFD3C1C837B23A1CB196ED7F9FADB333C5CFE4A462BE32ADCD67BFB6AC1, - p, - ).sqrt() - self.assertTrue(issubclass(w[0].category, NonResidueWarning)) - self.assertEqual(Mod(0, p).sqrt(), Mod(0, p)) - q = 0x75D44FEE9A71841AE8403C0C251FBAD - self.assertIn( - Mod(0x591E0DB18CF1BD81A11B2985A821EB3, q).sqrt(), - (0x113B41A1A2B73F636E73BE3F9A3716E, 0x64990E4CF7BA44B779CC7DCC8AE8A3F), - ) - getconfig().ec.non_residue_action = "error" - - def test_eq(self): - self.assertEqual(Mod(1, 7), 1) - self.assertNotEqual(Mod(1, 7), "1") - self.assertEqual(Mod(1, 7), Mod(1, 7)) - self.assertNotEqual(Mod(1, 7), Mod(5, 7)) - self.assertNotEqual(Mod(1, 7), Mod(1, 5)) - - def test_pow(self): - a = Mod(5, 7) - self.assertEqual(a ** (-1), a.inverse()) - self.assertEqual(a ** 0, Mod(1, 7)) - self.assertEqual(a ** (-2), a.inverse() ** 2) - - def test_wrong_mod(self): - a = Mod(5, 7) - b = Mod(4, 11) - with self.assertRaises(ValueError): - a + b - - def test_wrong_pow(self): - a = Mod(5, 7) - c = Mod(4, 11) - with self.assertRaises(TypeError): - a ** c - - def test_other(self): - a = Mod(5, 7) - b = Mod(3, 7) - self.assertEqual(int(-a), 2) - self.assertEqual(str(a), "5") - self.assertEqual(6 - a, Mod(1, 7)) - self.assertNotEqual(a, b) - self.assertEqual(a / b, Mod(4, 7)) - self.assertEqual(a // b, Mod(4, 7)) - self.assertEqual(5 / b, Mod(4, 7)) - self.assertEqual(5 // b, Mod(4, 7)) - self.assertEqual(a / 3, Mod(4, 7)) - self.assertEqual(a // 3, Mod(4, 7)) - self.assertEqual(divmod(a, b), (Mod(1, 7), Mod(2, 7))) - self.assertEqual(a + b, Mod(1, 7)) - self.assertEqual(5 + b, Mod(1, 7)) - self.assertEqual(a + 3, Mod(1, 7)) - self.assertNotEqual(a, 6) - self.assertIsNotNone(hash(a)) - - def test_undefined(self): - u = Undefined() - for k, meth in u.__class__.__dict__.items(): - if k in ( + getconfig().ec.non_residue_action = "warning" + with warnings.catch_warnings(record=True) as w: + Mod( + 0x702BDAFD3C1C837B23A1CB196ED7F9FADB333C5CFE4A462BE32ADCD67BFB6AC1, p + ).sqrt() + assert issubclass(w[0].category, NonResidueWarning) + getconfig().ec.non_residue_action = "ignore" + Mod( + 0x702BDAFD3C1C837B23A1CB196ED7F9FADB333C5CFE4A462BE32ADCD67BFB6AC1, p + ).sqrt() + with TemporaryConfig() as cfg: + cfg.ec.non_residue_action = "warning" + with warnings.catch_warnings(record=True) as w: + Mod( + 0x702BDAFD3C1C837B23A1CB196ED7F9FADB333C5CFE4A462BE32ADCD67BFB6AC1, + p, + ).sqrt() + assert issubclass(w[0].category, NonResidueWarning) + assert Mod(0, p).sqrt() == Mod(0, p) + q = 0x75D44FEE9A71841AE8403C0C251FBAD + assert Mod(0x591E0DB18CF1BD81A11B2985A821EB3, q).sqrt() in \ + (0x113B41A1A2B73F636E73BE3F9A3716E, 0x64990E4CF7BA44B779CC7DCC8AE8A3F) + getconfig().ec.non_residue_action = "error" + + +def test_eq(): + assert Mod(1, 7) == 1 + assert Mod(1, 7) != "1" + assert Mod(1, 7) == Mod(1, 7) + assert Mod(1, 7) != Mod(5, 7) + assert Mod(1, 7) != Mod(1, 5) + + +def test_pow(): + a = Mod(5, 7) + assert a ** (-1) == a.inverse() + assert a ** 0 == Mod(1, 7) + assert a ** (-2) == a.inverse() ** 2 + + +def test_wrong_mod(): + a = Mod(5, 7) + b = Mod(4, 11) + with pytest.raises(ValueError): + a + b + + +def test_wrong_pow(): + a = Mod(5, 7) + c = Mod(4, 11) + with pytest.raises(TypeError): + a ** c + + +def test_other(): + a = Mod(5, 7) + b = Mod(3, 7) + assert int(-a) == 2 + assert str(a) == "5" + assert 6 - a == Mod(1, 7) + assert a != b + assert a / b == Mod(4, 7) + assert a // b == Mod(4, 7) + assert 5 / b == Mod(4, 7) + assert 5 // b == Mod(4, 7) + assert a / 3 == Mod(4, 7) + assert a // 3 == Mod(4, 7) + assert divmod(a, b) == (Mod(1, 7), Mod(2, 7)) + assert a + b == Mod(1, 7) + assert 5 + b == Mod(1, 7) + assert a + 3 == Mod(1, 7) + assert a != 6 + assert hash(a) is not None + + +def test_undefined(): + u = Undefined() + for k, meth in u.__class__.__dict__.items(): + if k in ( "__module__", "__new__", "__init__", @@ -179,52 +176,54 @@ class ModTests(TestCase): "__slots__", "x", "n" - ): - continue - args = [5 for _ in range(meth.__code__.co_argcount - 1)] - if k == "__repr__": - self.assertEqual(meth(u), "Undefined") - elif k in ("__eq__", "__ne__"): - assert not meth(u, *args) - else: - try: - res = meth(u, *args) - self.assertEqual(res, NotImplemented) - except NotImplementedError: - pass - - def test_implementation(self): - if not has_gmp: - self.skipTest("Only makes sense if more Mod implementations are available.") - with TemporaryConfig() as cfg: - cfg.ec.mod_implementation = "python" - self.assertIsInstance(Mod(5, 7), RawMod) - - def test_symbolic(self): - x = symbols("x") - p = 13 - k = FF(p) - sx = SymbolicMod(x, p) - a = k(3) - b = k(5) - r = sx * a + b - self.assertIsInstance(r, SymbolicMod) - self.assertEqual(r.n, p) - sa = SymbolicMod(a, p) - sb = SymbolicMod(b, p) - self.assertEqual(sa, 3) - self.assertEqual(sa.inverse(), SymbolicMod(k(9), p)) - self.assertEqual(1 / sa, SymbolicMod(k(9), p)) - self.assertEqual(sa + sb, 8) - self.assertEqual(1 + sa, 4) - self.assertEqual(sa - 1, 2) - self.assertEqual(1 - sa, 11) - self.assertEqual(sa + 1, 4) - self.assertEqual(-sa, 10) - self.assertEqual(sa / 2, 8) - self.assertEqual(2 / sa, 5) - self.assertEqual(sa // 2, 8) - self.assertEqual(2 // sa, 5) - self.assertEqual(int(sa), 3) - self.assertNotEqual(sa, sb) - self.assertIsNotNone(hash(sa)) + ): + continue + args = [5 for _ in range(meth.__code__.co_argcount - 1)] + if k == "__repr__": + assert meth(u) == "Undefined" + elif k in ("__eq__", "__ne__"): + assert not meth(u, *args) + else: + try: + res = meth(u, *args) + assert res == NotImplemented + except NotImplementedError: + pass + + +def test_implementation(): + if not has_gmp: + pytest.skip("Only makes sense if more Mod implementations are available.") + with TemporaryConfig() as cfg: + cfg.ec.mod_implementation = "python" + assert isinstance(Mod(5, 7), RawMod) + + +def test_symbolic(): + x = symbols("x") + p = 13 + k = FF(p) + sx = SymbolicMod(x, p) + a = k(3) + b = k(5) + r = sx * a + b + assert isinstance(r, SymbolicMod) + assert r.n == p + sa = SymbolicMod(a, p) + sb = SymbolicMod(b, p) + assert sa == 3 + assert sa.inverse() == SymbolicMod(k(9), p) + assert 1 / sa == SymbolicMod(k(9), p) + assert sa + sb == 8 + assert 1 + sa == 4 + assert sa - 1 == 2 + assert 1 - sa == 11 + assert sa + 1 == 4 + assert -sa == 10 + assert sa / 2 == 8 + assert 2 / sa == 5 + assert sa // 2 == 8 + assert 2 // sa == 5 + assert int(sa) == 3 + assert sa != sb + assert hash(sa) is not None diff --git a/test/ec/test_model.py b/test/ec/test_model.py index d1c03c3..d5c5afe 100644 --- a/test/ec/test_model.py +++ b/test/ec/test_model.py @@ -1,5 +1,3 @@ -from unittest import TestCase - from pyecsca.ec.model import ( ShortWeierstrassModel, MontgomeryModel, @@ -8,9 +6,8 @@ from pyecsca.ec.model import ( ) -class CurveModelTests(TestCase): - def test_load(self): - self.assertGreater(len(ShortWeierstrassModel().coordinates), 0) - self.assertGreater(len(MontgomeryModel().coordinates), 0) - self.assertGreater(len(EdwardsModel().coordinates), 0) - self.assertGreater(len(TwistedEdwardsModel().coordinates), 0) +def test_load(): + assert len(ShortWeierstrassModel().coordinates) > 0 + assert len(MontgomeryModel().coordinates) > 0 + assert len(EdwardsModel().coordinates) > 0 + assert len(TwistedEdwardsModel().coordinates) > 0 diff --git a/test/ec/test_mult.py b/test/ec/test_mult.py index 5200520..91ef6dd 100644 --- a/test/ec/test_mult.py +++ b/test/ec/test_mult.py @@ -1,5 +1,6 @@ from unittest import TestCase +import pytest from parameterized import parameterized from pyecsca.ec.params import get_params @@ -13,320 +14,316 @@ from pyecsca.ec.mult import ( DifferentialLadderMultiplier, CoronMultiplier, ) -from pyecsca.ec.point import InfinityPoint +from pyecsca.ec.point import InfinityPoint, Point from .utils import cartesian -class ScalarMultiplierTests(TestCase): - def setUp(self): - self.secp128r1 = get_params("secg", "secp128r1", "projective") - self.base = self.secp128r1.generator - self.coords = self.secp128r1.curve.coordinate_model +def get_formulas(coords, *names): + return [coords.formulas[name] for name in names if name is not None] - self.curve25519 = get_params("other", "Curve25519", "xz") - self.base25519 = self.curve25519.generator - self.coords25519 = self.curve25519.curve.coordinate_model - def get_formulas(self, coords, *names): - return [coords.formulas[name] for name in names if name is not None] +def assert_pt_equality(one: Point, other: Point, scale): + if scale: + assert one == other + else: + assert one.equals(other) - def assertPointEquality(self, one, other, scale): - if scale: - self.assertEqual(one, other) - else: - assert one.equals(other) - def do_basic_test( - self, mult_class, params, base, add, dbl, scale, neg=None, **kwargs - ): - mult = mult_class( - *self.get_formulas(params.curve.coordinate_model, add, dbl, neg, scale), - **kwargs - ) - mult.init(params, base) - res = mult.multiply(314) - other = mult.multiply(157) - mult.init(params, other) - other = mult.multiply(2) - self.assertPointEquality(res, other, scale) - mult.init(params, base) - self.assertEqual(InfinityPoint(params.curve.coordinate_model), mult.multiply(0)) - return res - - @parameterized.expand( - [ - ("scaled", "add-1998-cmo", "dbl-1998-cmo", "z"), - ("complete", "add-2016-rcb", "dbl-2016-rcb", None), - ("none", "add-1998-cmo", "dbl-1998-cmo", None), - ] +def do_basic_test( + mult_class, params, base, add, dbl, scale, neg=None, **kwargs +): + mult = mult_class( + *get_formulas(params.curve.coordinate_model, add, dbl, neg, scale), + **kwargs ) - def test_rtl(self, name, add, dbl, scale): - self.do_basic_test(RTLMultiplier, self.secp128r1, self.base, add, dbl, scale) - - @parameterized.expand( - [ - ("scaled", "add-1998-cmo", "dbl-1998-cmo", "z"), - ("complete", "add-2016-rcb", "dbl-2016-rcb", None), - ("none", "add-1998-cmo", "dbl-1998-cmo", None), - ] + mult.init(params, base) + res = mult.multiply(314) + other = mult.multiply(157) + mult.init(params, other) + other = mult.multiply(2) + assert_pt_equality(res, other, scale) + mult.init(params, base) + assert InfinityPoint(params.curve.coordinate_model) == mult.multiply(0) + return res + + +@pytest.mark.parametrize("name,add,dbl,scale", + [ + ("scaled", "add-1998-cmo", "dbl-1998-cmo", "z"), + ("complete", "add-2016-rcb", "dbl-2016-rcb", None), + ("none", "add-1998-cmo", "dbl-1998-cmo", None), + ]) +def test_rtl(secp128r1, name, add, dbl, scale): + do_basic_test(RTLMultiplier, secp128r1, secp128r1.generator, add, dbl, scale) + + +@pytest.mark.parametrize("name,add,dbl,scale", + [ + ("scaled", "add-1998-cmo", "dbl-1998-cmo", "z"), + ("complete", "add-2016-rcb", "dbl-2016-rcb", None), + ("none", "add-1998-cmo", "dbl-1998-cmo", None), + ]) +def test_ltr(secp128r1, name, add, dbl, scale): + a = do_basic_test( + LTRMultiplier, secp128r1, secp128r1.generator, add, dbl, scale ) - def test_ltr(self, name, add, dbl, scale): - a = self.do_basic_test( - LTRMultiplier, self.secp128r1, self.base, add, dbl, scale - ) - b = self.do_basic_test( - LTRMultiplier, self.secp128r1, self.base, add, dbl, scale, always=True - ) - c = self.do_basic_test( - LTRMultiplier, self.secp128r1, self.base, add, dbl, scale, complete=False - ) - d = self.do_basic_test( - LTRMultiplier, - self.secp128r1, - self.base, - add, - dbl, - scale, - always=True, - complete=False, - ) - self.assertPointEquality(a, b, scale) - self.assertPointEquality(b, c, scale) - self.assertPointEquality(c, d, scale) - - @parameterized.expand( - [ - ("scaled", "add-1998-cmo", "dbl-1998-cmo", "z"), - ("complete", "add-2016-rcb", "dbl-2016-rcb", None), - ("none", "add-1998-cmo", "dbl-1998-cmo", None), - ] + b = do_basic_test( + LTRMultiplier, secp128r1, secp128r1.generator, add, dbl, scale, always=True ) - def test_coron(self, name, add, dbl, scale): - self.do_basic_test(CoronMultiplier, self.secp128r1, self.base, add, dbl, scale) - - def test_ladder(self): - a = self.do_basic_test( - LadderMultiplier, - self.curve25519, - self.base25519, - "ladd-1987-m", - "dbl-1987-m", - "scale", - ) - b = self.do_basic_test( - LadderMultiplier, - self.curve25519, - self.base25519, - "ladd-1987-m", - "dbl-1987-m", - "scale", - complete=False, - ) - self.assertPointEquality(a, b, True) - - @parameterized.expand( - [ - ("scaled", "add-1998-cmo", "dbl-1998-cmo", "z"), - ("complete", "add-2016-rcb", "dbl-2016-rcb", None), - ("none", "add-1998-cmo", "dbl-1998-cmo", None), - ] + c = do_basic_test( + LTRMultiplier, secp128r1, secp128r1.generator, add, dbl, scale, complete=False ) - def test_simple_ladder(self, name, add, dbl, scale): - self.do_basic_test( - SimpleLadderMultiplier, self.secp128r1, self.base, add, dbl, scale - ) + d = do_basic_test( + LTRMultiplier, + secp128r1, + secp128r1.generator, + add, + dbl, + scale, + always=True, + complete=False, + ) + assert_pt_equality(a, b, scale) + assert_pt_equality(b, c, scale) + assert_pt_equality(c, d, scale) + - @parameterized.expand( - [ - ("15", 15, True), - ("15", 15, False), - ("2355498743", 2355498743, True), - ("2355498743", 2355498743, False), - ( - "325385790209017329644351321912443757746", - 325385790209017329644351321912443757746, - True, - ), - ( - "325385790209017329644351321912443757746", - 325385790209017329644351321912443757746, - False, - ), - ] +@pytest.mark.parametrize("name,add,dbl,scale", + [ + ("scaled", "add-1998-cmo", "dbl-1998-cmo", "z"), + ("complete", "add-2016-rcb", "dbl-2016-rcb", None), + ("none", "add-1998-cmo", "dbl-1998-cmo", None), + ] + ) +def test_coron(secp128r1, name, add, dbl, scale): + do_basic_test(CoronMultiplier, secp128r1, secp128r1.generator, add, dbl, scale) + + +def test_ladder(curve25519): + a = do_basic_test( + LadderMultiplier, + curve25519, + curve25519.generator, + "ladd-1987-m", + "dbl-1987-m", + "scale", ) - def test_ladder_differential(self, name, num, complete): - ladder = LadderMultiplier( - self.coords25519.formulas["ladd-1987-m"], - self.coords25519.formulas["dbl-1987-m"], - self.coords25519.formulas["scale"], - complete=complete, - ) - differential = DifferentialLadderMultiplier( - self.coords25519.formulas["dadd-1987-m"], - self.coords25519.formulas["dbl-1987-m"], - self.coords25519.formulas["scale"], - complete=complete, - ) - ladder.init(self.curve25519, self.base25519) - res_ladder = ladder.multiply(num) - differential.init(self.curve25519, self.base25519) - res_differential = differential.multiply(num) - self.assertEqual(res_ladder, res_differential) - self.assertEqual(InfinityPoint(self.coords25519), differential.multiply(0)) - - @parameterized.expand( - [ - ("scaled", "add-1998-cmo", "dbl-1998-cmo", "neg", "z"), - ("complete", "add-2016-rcb", "dbl-2016-rcb", "neg", None), - ("none", "add-1998-cmo", "dbl-1998-cmo", "neg", None), - ] + b = do_basic_test( + LadderMultiplier, + curve25519, + curve25519.generator, + "ladd-1987-m", + "dbl-1987-m", + "scale", + complete=False, ) - def test_binary_naf(self, name, add, dbl, neg, scale): - self.do_basic_test( - BinaryNAFMultiplier, self.secp128r1, self.base, add, dbl, scale, neg - ) + assert_pt_equality(a, b, True) + - @parameterized.expand( - [ - ("scaled3", "add-1998-cmo", "dbl-1998-cmo", "neg", 3, "z"), - ("none3", "add-1998-cmo", "dbl-1998-cmo", "neg", 3, None), - ("complete3", "add-2016-rcb", "dbl-2016-rcb", "neg", 3, None), - ("scaled5", "add-1998-cmo", "dbl-1998-cmo", "neg", 5, "z"), - ("none5", "add-1998-cmo", "dbl-1998-cmo", "neg", 5, None), - ("complete5", "add-2016-rcb", "dbl-2016-rcb", "neg", 5, None), - ] +@pytest.mark.parametrize("name,add,dbl,scale", + [ + ("scaled", "add-1998-cmo", "dbl-1998-cmo", "z"), + ("complete", "add-2016-rcb", "dbl-2016-rcb", None), + ("none", "add-1998-cmo", "dbl-1998-cmo", None), + ]) +def test_simple_ladder(secp128r1, name, add, dbl, scale): + do_basic_test( + SimpleLadderMultiplier, secp128r1, secp128r1.generator, add, dbl, scale ) - def test_window_naf(self, name, add, dbl, neg, width, scale): - formulas = self.get_formulas(self.coords, add, dbl, neg, scale) - mult = WindowNAFMultiplier(*formulas[:3], width, *formulas[3:]) - mult.init(self.secp128r1, self.base) - res = mult.multiply(157 * 789) - other = mult.multiply(157) - mult.init(self.secp128r1, other) - other = mult.multiply(789) - self.assertPointEquality(res, other, scale) - mult.init(self.secp128r1, self.base) - self.assertEqual(InfinityPoint(self.coords), mult.multiply(0)) - - mult = WindowNAFMultiplier( - *formulas[:3], width, *formulas[3:], precompute_negation=True - ) - mult.init(self.secp128r1, self.base) - res_precompute = mult.multiply(157 * 789) - self.assertPointEquality(res_precompute, res, scale) - - @parameterized.expand( - cartesian( - [ - ("10", 10), - ("2355498743", 2355498743), - ( - "325385790209017329644351321912443757746", - 325385790209017329644351321912443757746, - ), - ], - [("add-1998-cmo", "dbl-1998-cmo"), ("add-2016-rcb", "dbl-2016-rcb")], - ) + + +@pytest.mark.parametrize("name,num,complete", + [ + ("15", 15, True), + ("15", 15, False), + ("2355498743", 2355498743, True), + ("2355498743", 2355498743, False), + ( + "325385790209017329644351321912443757746", + 325385790209017329644351321912443757746, + True, + ), + ( + "325385790209017329644351321912443757746", + 325385790209017329644351321912443757746, + False, + ), + ]) +def test_ladder_differential(curve25519, name, num, complete): + ladder = LadderMultiplier( + curve25519.curve.coordinate_model.formulas["ladd-1987-m"], + curve25519.curve.coordinate_model.formulas["dbl-1987-m"], + curve25519.curve.coordinate_model.formulas["scale"], + complete=complete, ) - def test_basic_multipliers(self, name, num, add, dbl): - ltr = LTRMultiplier( - self.coords.formulas[add], - self.coords.formulas[dbl], - self.coords.formulas["z"], - ) - with self.assertRaises(ValueError): - ltr.multiply(1) - ltr.init(self.secp128r1, self.base) - res_ltr = ltr.multiply(num) - rtl = RTLMultiplier( - self.coords.formulas[add], - self.coords.formulas["dbl-1998-cmo"], - self.coords.formulas["z"], - ) - with self.assertRaises(ValueError): - rtl.multiply(1) - rtl.init(self.secp128r1, self.base) - res_rtl = rtl.multiply(num) - self.assertEqual(res_ltr, res_rtl) - - ltr_always = LTRMultiplier( - self.coords.formulas[add], - self.coords.formulas[dbl], - self.coords.formulas["z"], - always=True, - ) - rtl_always = RTLMultiplier( - self.coords.formulas[add], - self.coords.formulas[dbl], - self.coords.formulas["z"], - always=True, - ) - ltr_always.init(self.secp128r1, self.base) - rtl_always.init(self.secp128r1, self.base) - res_ltr_always = ltr_always.multiply(num) - res_rtl_always = rtl_always.multiply(num) - self.assertEqual(res_ltr, res_ltr_always) - self.assertEqual(res_rtl, res_rtl_always) - - bnaf = BinaryNAFMultiplier( - self.coords.formulas[add], - self.coords.formulas[dbl], - self.coords.formulas["neg"], - self.coords.formulas["z"], - ) - with self.assertRaises(ValueError): - bnaf.multiply(1) - bnaf.init(self.secp128r1, self.base) - res_bnaf = bnaf.multiply(num) - self.assertEqual(res_bnaf, res_ltr) - - wnaf = WindowNAFMultiplier( - self.coords.formulas[add], - self.coords.formulas[dbl], - self.coords.formulas["neg"], - 3, - self.coords.formulas["z"], - ) - with self.assertRaises(ValueError): - wnaf.multiply(1) - wnaf.init(self.secp128r1, self.base) - res_wnaf = wnaf.multiply(num) - self.assertEqual(res_wnaf, res_ltr) - - ladder = SimpleLadderMultiplier( - self.coords.formulas[add], - self.coords.formulas[dbl], - self.coords.formulas["z"], - ) - with self.assertRaises(ValueError): - ladder.multiply(1) - ladder.init(self.secp128r1, self.base) - res_ladder = ladder.multiply(num) - self.assertEqual(res_ladder, res_ltr) - - coron = CoronMultiplier( - self.coords.formulas[add], - self.coords.formulas[dbl], - self.coords.formulas["z"], - ) - with self.assertRaises(ValueError): - coron.multiply(1) - coron.init(self.secp128r1, self.base) - res_coron = coron.multiply(num) - self.assertEqual(res_coron, res_ltr) - - def test_init_fail(self): - mult = DifferentialLadderMultiplier( - self.coords25519.formulas["dadd-1987-m"], - self.coords25519.formulas["dbl-1987-m"], - self.coords25519.formulas["scale"], + differential = DifferentialLadderMultiplier( + curve25519.curve.coordinate_model.formulas["dadd-1987-m"], + curve25519.curve.coordinate_model.formulas["dbl-1987-m"], + curve25519.curve.coordinate_model.formulas["scale"], + complete=complete, + ) + ladder.init(curve25519, curve25519.generator) + res_ladder = ladder.multiply(num) + differential.init(curve25519, curve25519.generator) + res_differential = differential.multiply(num) + assert res_ladder == res_differential + assert InfinityPoint(curve25519.curve.coordinate_model) == differential.multiply(0) + + +@pytest.mark.parametrize("name,add,dbl,neg,scale", + [ + ("scaled", "add-1998-cmo", "dbl-1998-cmo", "neg", "z"), + ("complete", "add-2016-rcb", "dbl-2016-rcb", "neg", None), + ("none", "add-1998-cmo", "dbl-1998-cmo", "neg", None), + ]) +def test_binary_naf(secp128r1, name, add, dbl, neg, scale): + do_basic_test( + BinaryNAFMultiplier, secp128r1, secp128r1.generator, add, dbl, scale, neg + ) + + +@pytest.mark.parametrize("name,add,dbl,neg,width,scale", + [ + ("scaled3", "add-1998-cmo", "dbl-1998-cmo", "neg", 3, "z"), + ("none3", "add-1998-cmo", "dbl-1998-cmo", "neg", 3, None), + ("complete3", "add-2016-rcb", "dbl-2016-rcb", "neg", 3, None), + ("scaled5", "add-1998-cmo", "dbl-1998-cmo", "neg", 5, "z"), + ("none5", "add-1998-cmo", "dbl-1998-cmo", "neg", 5, None), + ("complete5", "add-2016-rcb", "dbl-2016-rcb", "neg", 5, None), + ]) +def test_window_naf(secp128r1, name, add, dbl, neg, width, scale): + formulas = get_formulas(secp128r1.curve.coordinate_model, add, dbl, neg, scale) + mult = WindowNAFMultiplier(*formulas[:3], width, *formulas[3:]) + mult.init(secp128r1, secp128r1.generator) + res = mult.multiply(157 * 789) + other = mult.multiply(157) + mult.init(secp128r1, other) + other = mult.multiply(789) + assert_pt_equality(res, other, scale) + mult.init(secp128r1, secp128r1.generator) + assert InfinityPoint(secp128r1.curve.coordinate_model) == mult.multiply(0) + + mult = WindowNAFMultiplier( + *formulas[:3], width, *formulas[3:], precompute_negation=True + ) + mult.init(secp128r1, secp128r1.generator) + res_precompute = mult.multiply(157 * 789) + assert_pt_equality(res_precompute, res, scale) + + +@pytest.mark.parametrize("name,num,add,dbl", + cartesian( + [ + ("10", 10), + ("2355498743", 2355498743), + ( + "325385790209017329644351321912443757746", + 325385790209017329644351321912443757746, + ), + ], + [("add-1998-cmo", "dbl-1998-cmo"), ("add-2016-rcb", "dbl-2016-rcb")], + ) + ) +def test_basic_multipliers(secp128r1, name, num, add, dbl): + ltr = LTRMultiplier( + secp128r1.curve.coordinate_model.formulas[add], + secp128r1.curve.coordinate_model.formulas[dbl], + secp128r1.curve.coordinate_model.formulas["z"], + ) + with pytest.raises(ValueError): + ltr.multiply(1) + ltr.init(secp128r1, secp128r1.generator) + res_ltr = ltr.multiply(num) + rtl = RTLMultiplier( + secp128r1.curve.coordinate_model.formulas[add], + secp128r1.curve.coordinate_model.formulas["dbl-1998-cmo"], + secp128r1.curve.coordinate_model.formulas["z"], + ) + with pytest.raises(ValueError): + rtl.multiply(1) + rtl.init(secp128r1, secp128r1.generator) + res_rtl = rtl.multiply(num) + assert res_ltr == res_rtl + + ltr_always = LTRMultiplier( + secp128r1.curve.coordinate_model.formulas[add], + secp128r1.curve.coordinate_model.formulas[dbl], + secp128r1.curve.coordinate_model.formulas["z"], + always=True, + ) + rtl_always = RTLMultiplier( + secp128r1.curve.coordinate_model.formulas[add], + secp128r1.curve.coordinate_model.formulas[dbl], + secp128r1.curve.coordinate_model.formulas["z"], + always=True, + ) + ltr_always.init(secp128r1, secp128r1.generator) + rtl_always.init(secp128r1, secp128r1.generator) + res_ltr_always = ltr_always.multiply(num) + res_rtl_always = rtl_always.multiply(num) + assert res_ltr == res_ltr_always + assert res_rtl == res_rtl_always + + bnaf = BinaryNAFMultiplier( + secp128r1.curve.coordinate_model.formulas[add], + secp128r1.curve.coordinate_model.formulas[dbl], + secp128r1.curve.coordinate_model.formulas["neg"], + secp128r1.curve.coordinate_model.formulas["z"], + ) + with pytest.raises(ValueError): + bnaf.multiply(1) + bnaf.init(secp128r1, secp128r1.generator) + res_bnaf = bnaf.multiply(num) + assert res_bnaf == res_ltr + + wnaf = WindowNAFMultiplier( + secp128r1.curve.coordinate_model.formulas[add], + secp128r1.curve.coordinate_model.formulas[dbl], + secp128r1.curve.coordinate_model.formulas["neg"], + 3, + secp128r1.curve.coordinate_model.formulas["z"], + ) + with pytest.raises(ValueError): + wnaf.multiply(1) + wnaf.init(secp128r1, secp128r1.generator) + res_wnaf = wnaf.multiply(num) + assert res_wnaf == res_ltr + + ladder = SimpleLadderMultiplier( + secp128r1.curve.coordinate_model.formulas[add], + secp128r1.curve.coordinate_model.formulas[dbl], + secp128r1.curve.coordinate_model.formulas["z"], + ) + with pytest.raises(ValueError): + ladder.multiply(1) + ladder.init(secp128r1, secp128r1.generator) + res_ladder = ladder.multiply(num) + assert res_ladder == res_ltr + + coron = CoronMultiplier( + secp128r1.curve.coordinate_model.formulas[add], + secp128r1.curve.coordinate_model.formulas[dbl], + secp128r1.curve.coordinate_model.formulas["z"], + ) + with pytest.raises(ValueError): + coron.multiply(1) + coron.init(secp128r1, secp128r1.generator) + res_coron = coron.multiply(num) + assert res_coron == res_ltr + + +def test_init_fail(curve25519, secp128r1): + mult = DifferentialLadderMultiplier( + curve25519.curve.coordinate_model.formulas["dadd-1987-m"], + curve25519.curve.coordinate_model.formulas["dbl-1987-m"], + curve25519.curve.coordinate_model.formulas["scale"], + ) + with pytest.raises(ValueError): + mult.init(secp128r1, secp128r1.generator) + + with pytest.raises(ValueError): + LadderMultiplier( + curve25519.curve.coordinate_model.formulas["ladd-1987-m"], + scl=curve25519.curve.coordinate_model.formulas["scale"], + complete=False, ) - with self.assertRaises(ValueError): - mult.init(self.secp128r1, self.base) - - with self.assertRaises(ValueError): - LadderMultiplier( - self.coords25519.formulas["ladd-1987-m"], - scl=self.coords25519.formulas["scale"], - complete=False, - ) diff --git a/test/ec/test_naf.py b/test/ec/test_naf.py index bdc176a..3e7c444 100644 --- a/test/ec/test_naf.py +++ b/test/ec/test_naf.py @@ -1,9 +1,6 @@ -from unittest import TestCase - from pyecsca.ec.naf import naf, wnaf -class NafTests(TestCase): - def test_nafs(self): - i = 0b1100110101001101011011 - self.assertListEqual(naf(i), wnaf(i, 2)) +def test_nafs(): + i = 0b1100110101001101011011 + assert naf(i) == wnaf(i, 2) diff --git a/test/ec/test_op.py b/test/ec/test_op.py index e418350..94edcc3 100644 --- a/test/ec/test_op.py +++ b/test/ec/test_op.py @@ -1,49 +1,30 @@ from ast import parse -from unittest import TestCase -from parameterized import parameterized - -from pyecsca.ec.formula import OpResult +import pytest from pyecsca.ec.mod import Mod from pyecsca.ec.op import CodeOp, OpType -class OpTests(TestCase): - @parameterized.expand( - [ - ("add", "x = a+b", "x = a+b", OpType.Add), - ("sub", "x = a-b", "x = a-b", OpType.Sub), - ("mul", "y = a*b", "y = a*b", OpType.Mult), - ("div", "z = a/b", "z = a/b", OpType.Div), - ("inv", "z = 1/b", "z = 1/b", OpType.Inv), - ("pow", "b = a**d", "b = a^d", OpType.Pow), - ("sqr", "b = a**2", "b = a^2", OpType.Sqr), - ("id1", "b = 7", "b = 7", OpType.Id), - ("id2", "b = a", "b = a", OpType.Id), - ] - ) - def test_str(self, name, module, result, op_type): - code = parse(module, mode="exec") - op = CodeOp(code) - self.assertEqual(str(op), result) - self.assertEqual(op.operator, op_type) +@pytest.mark.parametrize("name,module,result,op_type", + [("add", "x = a+b", "x = a+b", OpType.Add), ("sub", "x = a-b", "x = a-b", OpType.Sub), + ("mul", "y = a*b", "y = a*b", OpType.Mult), ("div", "z = a/b", "z = a/b", OpType.Div), + ("inv", "z = 1/b", "z = 1/b", OpType.Inv), ("pow", "b = a**d", "b = a^d", OpType.Pow), + ("sqr", "b = a**2", "b = a^2", OpType.Sqr), ("id1", "b = 7", "b = 7", OpType.Id), + ("id2", "b = a", "b = a", OpType.Id), ]) +def test_str(name, module, result, op_type): + code = parse(module, mode="exec") + op = CodeOp(code) + assert str(op) == result + assert op.operator == op_type - @parameterized.expand( - [ - ("add", "x = a+b", {"a": Mod(5, 21), "b": Mod(7, 21)}, Mod(12, 21)), - ("sub", "x = a-b", {"a": Mod(7, 21), "b": Mod(5, 21)}, Mod(2, 21)), - ] - ) - def test_call(self, name, module, locals, result): - code = parse(module, mode="exec") - op = CodeOp(code) - res = op(**locals) - self.assertEqual(res, result) +@pytest.mark.parametrize("name,module,locals,result", + [("add", "x = a+b", {"a": Mod(5, 21), "b": Mod(7, 21)}, Mod(12, 21)), + ("sub", "x = a-b", {"a": Mod(7, 21), "b": Mod(5, 21)}, Mod(2, 21)), ]) +def test_call(name, module, locals, result): + code = parse(module, mode="exec") + op = CodeOp(code) + res = op(**locals) + assert res == result -class OpResultTests(TestCase): - def test_str(self): - for op in (OpType.Add, OpType.Sub, OpType.Mult, OpType.Div): - res = OpResult("X1", Mod(0, 5), op, Mod(2, 5), Mod(3, 5)) - self.assertEqual(str(res), "X1") - self.assertEqual(repr(res), f"X1 = 2{op.op_str}3") +# TODO: Add op_type tests diff --git a/test/ec/test_params.py b/test/ec/test_params.py index 9f18ded..5441ed0 100644 --- a/test/ec/test_params.py +++ b/test/ec/test_params.py @@ -3,6 +3,8 @@ from unittest import TestCase from parameterized import parameterized from importlib.resources import files, as_file +import pytest + import test.data.ec from pyecsca.ec.mod import Mod from pyecsca.ec.point import Point, InfinityPoint @@ -15,125 +17,131 @@ from pyecsca.ec.model import ShortWeierstrassModel from pyecsca.ec.curve import EllipticCurve -class DomainParameterTests(TestCase): - def setUp(self): - self.secp128r1 = get_params("secg", "secp128r1", "projective") - self.curve25519 = get_params("other", "Curve25519", "xz") - - def test_eq(self): - self.assertEqual(self.secp128r1, self.secp128r1) - self.assertNotEqual(self.secp128r1, self.curve25519) - self.assertNotEqual(self.secp128r1, None) - - def test_str(self): - self.assertEqual(str(self.secp128r1), "DomainParameters(secg/secp128r1)") - - @parameterized.expand( - [ - ("secg/secp128r1", "projective"), - ("secg/secp256r1", "projective"), - ("secg/secp521r1", "projective"), - ("other/Curve25519", "xz"), - ("other/Ed25519", "projective"), - ("other/Ed448", "projective"), - ("other/E-222", "projective"), - ] - ) - def test_get_params(self, name, coords): - params = get_params(*name.split("/"), coords) +def test_eq(secp128r1, curve25519): + assert secp128r1 == secp128r1 + assert secp128r1 != curve25519 + assert secp128r1 is not None + + +def test_str(secp128r1): + assert str(secp128r1) == "DomainParameters(secg/secp128r1)" + + +@pytest.mark.parametrize("name,coords", + [ + ("secg/secp128r1", "projective"), + ("secg/secp256r1", "projective"), + ("secg/secp521r1", "projective"), + ("other/Curve25519", "xz"), + ("other/Ed25519", "projective"), + ("other/Ed448", "projective"), + ("other/E-222", "projective"), + ]) +def test_get_params(name, coords): + params = get_params(*name.split("/"), coords) + try: + assert params.curve.is_on_curve(params.generator) + except NotImplementedError: + pass + + +@pytest.mark.parametrize("name,coords", + [ + ("anssi", "projective"), + ( + "brainpool", + lambda name: "projective" if name.endswith("r1") else "jacobian", + ), + ]) +def test_get_category(name, coords): + get_category(name, coords) + + +def test_load_params(): + with as_file(files(test.data.ec).joinpath("curve.json")) as path: + params = load_params(path, "projective") try: assert params.curve.is_on_curve(params.generator) except NotImplementedError: pass - @parameterized.expand( - [ - ("anssi", "projective"), - ( - "brainpool", - lambda name: "projective" if name.endswith("r1") else "jacobian", - ), - ] - ) - def test_get_category(self, name, coords): - get_category(name, coords) - - def test_load_params(self): - with as_file(files(test.data.ec).joinpath("curve.json")) as path: - params = load_params(path, "projective") - try: - assert params.curve.is_on_curve(params.generator) - except NotImplementedError: - pass - - def test_load_params_ectester(self): - with as_file(files(test.data.ec).joinpath("ectester_secp128r1.csv")) as path: - params = load_params_ectester(path, "projective") - assert params.curve.is_on_curve(params.generator) - self.assertEqual(params, self.secp128r1) - def test_load_params_ecgen(self): - with as_file(files(test.data.ec).joinpath("ecgen_secp128r1.json")) as path: - params = load_params_ecgen(path, "projective") - assert params.curve.is_on_curve(params.generator) - self.assertEqual(params, self.secp128r1) - - def test_load_category(self): - with as_file(files(test.data.ec).joinpath("curves.json")) as path: - category = load_category(path, "yz") - self.assertEqual(len(category), 1) - - @parameterized.expand( - [ - ("no_category/some", "else"), - ("secg/no_curve", "else"), - ("secg/secp128r1", "some"), - ] - ) - def test_unknown(self, name, coords): - with self.assertRaises(ValueError): - get_params(*name.split("/"), coords) - - def test_assumption(self): - with self.assertRaises(UnsatisfiedAssumptionError): - get_params("secg", "secp128r1", "projective-1") - with TemporaryConfig() as cfg: - cfg.ec.unsatisfied_coordinate_assumption_action = "ignore" - params = get_params("secg", "secp128r1", "projective-1") - self.assertIsNotNone(params) - self.assertIsNotNone(get_params("secg", "secp128r1", "projective-3")) - - def test_infty(self): - with self.assertRaises(ValueError): - get_params("other", "Ed25519", "modified", False) - self.assertIsNotNone(get_params("secg", "secp128r1", "projective", False)) - - def test_no_binary(self): - with self.assertRaises(ValueError): - get_params("secg", "sect163r1", "something") - - def test_no_extension(self): - with self.assertRaises(ValueError): - get_params("other", "Fp254n2BNa", "something") - - def test_affine(self): - aff = get_params("secg", "secp128r1", "affine") - self.assertIsInstance(aff.curve.coordinate_model, AffineCoordinateModel) - - def test_custom_params(self): - model = ShortWeierstrassModel() - coords = model.coordinates["projective"] - p = 0xd7d1247f - a = Mod(0xa4a44016, p) - b = Mod(0x73f76716, p) - n = 0xd7d2a475 - h = 1 - gx, gy, gz = Mod(0x54eed6d7, p), Mod(0x6f1e55ac, p), Mod(1, p) - generator = Point(coords, X=gx, Y=gy, Z=gz) - neutral = InfinityPoint(coords) - - curve = EllipticCurve(model, coords, p, neutral, {"a": a, "b": b}) - params = DomainParameters(curve, generator, n, h) - self.assertIsNotNone(params) - res = params.curve.affine_double(generator.to_affine()) - self.assertIsNotNone(res) +def test_load_params_ectester(secp128r1): + with as_file(files(test.data.ec).joinpath("ectester_secp128r1.csv")) as path: + params = load_params_ectester(path, "projective") + assert params.curve.is_on_curve(params.generator) + assert params == secp128r1 + + +def test_load_params_ecgen(secp128r1): + with as_file(files(test.data.ec).joinpath("ecgen_secp128r1.json")) as path: + params = load_params_ecgen(path, "projective") + assert params.curve.is_on_curve(params.generator) + assert params == secp128r1 + + +def test_load_category(): + with as_file(files(test.data.ec).joinpath("curves.json")) as path: + category = load_category(path, "yz") + assert len(category) == 1 + + +@pytest.mark.parametrize("name,coords", + [ + ("no_category/some", "else"), + ("secg/no_curve", "else"), + ("secg/secp128r1", "some"), + ]) +def test_unknown(name, coords): + with pytest.raises(ValueError): + get_params(*name.split("/"), coords) + + +def test_assumption(): + with pytest.raises(UnsatisfiedAssumptionError): + get_params("secg", "secp128r1", "projective-1") + with TemporaryConfig() as cfg: + cfg.ec.unsatisfied_coordinate_assumption_action = "ignore" + params = get_params("secg", "secp128r1", "projective-1") + assert params is not None + assert get_params("secg", "secp128r1", "projective-3") is not None + + +def test_infty(): + with pytest.raises(ValueError): + get_params("other", "Ed25519", "modified", False) + assert get_params("secg", "secp128r1", "projective", False) is not None + + +def test_no_binary(): + with pytest.raises(ValueError): + get_params("secg", "sect163r1", "something") + + +def test_no_extension(): + with pytest.raises(ValueError): + get_params("other", "Fp254n2BNa", "something") + + +def test_affine(): + aff = get_params("secg", "secp128r1", "affine") + assert isinstance(aff.curve.coordinate_model, AffineCoordinateModel) + + +def test_custom_params(): + model = ShortWeierstrassModel() + coords = model.coordinates["projective"] + p = 0xd7d1247f + a = Mod(0xa4a44016, p) + b = Mod(0x73f76716, p) + n = 0xd7d2a475 + h = 1 + gx, gy, gz = Mod(0x54eed6d7, p), Mod(0x6f1e55ac, p), Mod(1, p) + generator = Point(coords, X=gx, Y=gy, Z=gz) + neutral = InfinityPoint(coords) + + curve = EllipticCurve(model, coords, p, neutral, {"a": a, "b": b}) + params = DomainParameters(curve, generator, n, h) + assert params is not None + res = params.curve.affine_double(generator.to_affine()) + assert res is not None diff --git a/test/ec/test_point.py b/test/ec/test_point.py index b2096be..b09ca30 100644 --- a/test/ec/test_point.py +++ b/test/ec/test_point.py @@ -5,6 +5,7 @@ from pyecsca.ec.params import get_params from pyecsca.ec.mod import Mod from pyecsca.ec.model import ShortWeierstrassModel, MontgomeryModel from pyecsca.ec.point import Point, InfinityPoint +import pytest class PointTests(TestCase): @@ -14,131 +15,146 @@ class PointTests(TestCase): self.coords = self.secp128r1.curve.coordinate_model self.affine = AffineCoordinateModel(ShortWeierstrassModel()) - def test_construction(self): - with self.assertRaises(ValueError): - Point(self.coords) - - def test_to_affine(self): - pt = Point( - self.coords, - X=Mod(0x161FF7528B899B2D0C28607CA52C5B86, self.secp128r1.curve.prime), - Y=Mod(0xCF5AC8395BAFEB13C02DA292DDED7A83, self.secp128r1.curve.prime), - Z=Mod(1, self.secp128r1.curve.prime), - ) - affine = pt.to_affine() - - self.assertIsInstance(affine.coordinate_model, AffineCoordinateModel) - self.assertSetEqual(set(affine.coords.keys()), set(self.affine.variables)) - self.assertEqual(affine.coords["x"], pt.coords["X"]) - self.assertEqual(affine.coords["y"], pt.coords["Y"]) - self.assertEqual(affine.to_affine(), affine) - - affine = InfinityPoint(self.coords).to_affine() - self.assertIsInstance(affine, InfinityPoint) - - def test_to_model(self): - affine = Point( - self.affine, - x=Mod(0xABCD, self.secp128r1.curve.prime), - y=Mod(0xEF, self.secp128r1.curve.prime), - ) - projective_model = self.coords - other = affine.to_model(projective_model, self.secp128r1.curve) - - self.assertEqual(other.coordinate_model, projective_model) - self.assertSetEqual(set(other.coords.keys()), set(projective_model.variables)) - self.assertEqual(other.coords["X"], affine.coords["x"]) - self.assertEqual(other.coords["Y"], affine.coords["y"]) - self.assertEqual(other.coords["Z"], Mod(1, self.secp128r1.curve.prime)) - - infty = InfinityPoint(AffineCoordinateModel(self.secp128r1.curve.model)) - other_infty = infty.to_model(self.coords, self.secp128r1.curve) - self.assertIsInstance(other_infty, InfinityPoint) - - with self.assertRaises(ValueError): - self.base.to_model(self.coords, self.secp128r1.curve) - - def test_to_from_affine(self): - pt = Point( - self.coords, - X=Mod(0x161FF7528B899B2D0C28607CA52C5B86, self.secp128r1.curve.prime), - Y=Mod(0xCF5AC8395BAFEB13C02DA292DDED7A83, self.secp128r1.curve.prime), - Z=Mod(1, self.secp128r1.curve.prime), - ) - other = pt.to_affine().to_model(self.coords, self.secp128r1.curve) - self.assertEqual(pt, other) - - def test_equals(self): - pt = Point( - self.coords, - X=Mod(0x4, self.secp128r1.curve.prime), - Y=Mod(0x6, self.secp128r1.curve.prime), - Z=Mod(2, self.secp128r1.curve.prime), - ) - other = Point( - self.coords, - X=Mod(0x2, self.secp128r1.curve.prime), - Y=Mod(0x3, self.secp128r1.curve.prime), - Z=Mod(1, self.secp128r1.curve.prime), - ) - third = Point( - self.coords, - X=Mod(0x5, self.secp128r1.curve.prime), - Y=Mod(0x3, self.secp128r1.curve.prime), - Z=Mod(1, self.secp128r1.curve.prime), - ) - self.assertTrue(pt.equals(other)) - self.assertNotEqual(pt, other) - self.assertFalse(pt.equals(2)) - self.assertNotEqual(pt, 2) - self.assertFalse(pt.equals(third)) - self.assertNotEqual(pt, third) - self.assertTrue(pt.equals_scaled(other)) - self.assertTrue(pt.equals_affine(other)) - self.assertFalse(pt.equals_scaled(third)) - - infty_one = InfinityPoint(self.coords) - infty_other = InfinityPoint(self.coords) - self.assertTrue(infty_one.equals(infty_other)) - self.assertTrue(infty_one.equals_affine(infty_other)) - self.assertTrue(infty_one.equals_scaled(infty_other)) - self.assertEqual(infty_one, infty_other) - self.assertFalse(pt.equals(infty_one)) - self.assertFalse(pt.equals_affine(infty_one)) - self.assertFalse(pt.equals_scaled(infty_one)) - - mont = MontgomeryModel() - different = Point( - mont.coordinates["xz"], - X=Mod( - 0x64DACCD2656420216545E5F65221EB, - 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, - ), - Z=Mod(1, 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA), - ) - self.assertFalse(pt.equals(different)) - self.assertNotEqual(pt, different) - - def test_bytes(self): - pt = Point( - self.coords, - X=Mod(0x4, self.secp128r1.curve.prime), - Y=Mod(0x6, self.secp128r1.curve.prime), - Z=Mod(2, self.secp128r1.curve.prime), - ) - self.assertEqual( - bytes(pt), - b"\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02", - ) - self.assertEqual(bytes(InfinityPoint(self.coords)), b"\x00") - - def test_iter(self): - pt = Point( - self.coords, - X=Mod(0x4, self.secp128r1.curve.prime), - Y=Mod(0x6, self.secp128r1.curve.prime), - Z=Mod(2, self.secp128r1.curve.prime), - ) - t = tuple(pt) - self.assertEqual(len(t), 3) - self.assertEqual(len(pt), 3) + +@pytest.fixture() +def coords(secp128r1): + return secp128r1.curve.coordinate_model + + +@pytest.fixture() +def affine_model(): + return AffineCoordinateModel(ShortWeierstrassModel()) + + +def test_construction(coords): + with pytest.raises(ValueError): + Point(coords) + + +def test_to_affine(secp128r1, coords, affine_model): + pt = Point( + coords, + X=Mod(0x161FF7528B899B2D0C28607CA52C5B86, secp128r1.curve.prime), + Y=Mod(0xCF5AC8395BAFEB13C02DA292DDED7A83, secp128r1.curve.prime), + Z=Mod(1, secp128r1.curve.prime), + ) + affine = pt.to_affine() + + assert isinstance(affine.coordinate_model, AffineCoordinateModel) + assert set(affine.coords.keys()) == set(affine_model.variables) + assert affine.coords["x"] == pt.coords["X"] + assert affine.coords["y"] == pt.coords["Y"] + assert affine.to_affine() == affine + + affine = InfinityPoint(coords).to_affine() + assert isinstance(affine, InfinityPoint) + + +def test_to_model(secp128r1, coords, affine_model): + affine = Point( + affine_model, + x=Mod(0xABCD, secp128r1.curve.prime), + y=Mod(0xEF, secp128r1.curve.prime), + ) + projective_model = coords + other = affine.to_model(projective_model, secp128r1.curve) + + assert other.coordinate_model == projective_model + assert set(other.coords.keys()) == set(projective_model.variables) + assert other.coords["X"] == affine.coords["x"] + assert other.coords["Y"] == affine.coords["y"] + assert other.coords["Z"] == Mod(1, secp128r1.curve.prime) + + infty = InfinityPoint(AffineCoordinateModel(secp128r1.curve.model)) + other_infty = infty.to_model(coords, secp128r1.curve) + assert isinstance(other_infty, InfinityPoint) + + with pytest.raises(ValueError): + secp128r1.generator.to_model(coords, secp128r1.curve) + + +def test_to_from_affine(secp128r1, coords): + pt = Point( + coords, + X=Mod(0x161FF7528B899B2D0C28607CA52C5B86, secp128r1.curve.prime), + Y=Mod(0xCF5AC8395BAFEB13C02DA292DDED7A83, secp128r1.curve.prime), + Z=Mod(1, secp128r1.curve.prime), + ) + other = pt.to_affine().to_model(coords, secp128r1.curve) + assert pt == other + + +def test_equals(secp128r1, coords): + pt = Point( + coords, + X=Mod(0x4, secp128r1.curve.prime), + Y=Mod(0x6, secp128r1.curve.prime), + Z=Mod(2, secp128r1.curve.prime), + ) + other = Point( + coords, + X=Mod(0x2, secp128r1.curve.prime), + Y=Mod(0x3, secp128r1.curve.prime), + Z=Mod(1, secp128r1.curve.prime), + ) + third = Point( + coords, + X=Mod(0x5, secp128r1.curve.prime), + Y=Mod(0x3, secp128r1.curve.prime), + Z=Mod(1, secp128r1.curve.prime), + ) + assert pt.equals(other) + assert pt != other + assert not pt.equals(2) + assert pt != 2 + assert not pt.equals(third) + assert pt != third + assert pt.equals_scaled(other) + assert pt.equals_affine(other) + assert not pt.equals_scaled(third) + + infty_one = InfinityPoint(coords) + infty_other = InfinityPoint(coords) + assert infty_one.equals(infty_other) + assert infty_one.equals_affine(infty_other) + assert infty_one.equals_scaled(infty_other) + assert infty_one == infty_other + assert not pt.equals(infty_one) + assert not pt.equals_affine(infty_one) + assert not pt.equals_scaled(infty_one) + + mont = MontgomeryModel() + different = Point( + mont.coordinates["xz"], + X=Mod( + 0x64DACCD2656420216545E5F65221EB, + 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, + ), + Z=Mod(1, 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA), + ) + assert not pt.equals(different) + assert pt != different + + +def test_bytes(secp128r1, coords): + pt = Point( + coords, + X=Mod(0x4, secp128r1.curve.prime), + Y=Mod(0x6, secp128r1.curve.prime), + Z=Mod(2, secp128r1.curve.prime), + ) + assert bytes(pt) == \ + b"\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02" + assert bytes(InfinityPoint(coords)) == b"\x00" + + +def test_iter(secp128r1, coords): + pt = Point( + coords, + X=Mod(0x4, secp128r1.curve.prime), + Y=Mod(0x6, secp128r1.curve.prime), + Z=Mod(2, secp128r1.curve.prime), + ) + t = tuple(pt) + assert len(t) == 3 + assert len(pt) == 3 diff --git a/test/ec/test_regress.py b/test/ec/test_regress.py index 13e6655..f032de2 100644 --- a/test/ec/test_regress.py +++ b/test/ec/test_regress.py @@ -1,10 +1,11 @@ from typing import cast -from unittest import TestCase, skip +from unittest import TestCase + +import pytest from sympy import symbols from pyecsca.ec.coordinates import AffineCoordinateModel from pyecsca.ec.curve import EllipticCurve -from pyecsca.ec.error import UnsatisfiedAssumptionError from pyecsca.ec.formula import AdditionFormula, DoublingFormula, ScalingFormula from pyecsca.ec.mod import Mod, SymbolicMod from pyecsca.ec.model import MontgomeryModel, EdwardsModel @@ -13,101 +14,101 @@ from pyecsca.ec.mult import LTRMultiplier from pyecsca.ec.point import Point, InfinityPoint -class RegressionTests(TestCase): - def test_issue_7(self): - secp128r1 = get_params("secg", "secp128r1", "projective") - base = secp128r1.generator - coords = secp128r1.curve.coordinate_model - add = cast(AdditionFormula, coords.formulas["add-1998-cmo"]) - dbl = cast(DoublingFormula, coords.formulas["dbl-1998-cmo"]) - scl = cast(ScalingFormula, coords.formulas["z"]) - mult = LTRMultiplier( - add, dbl, scl, always=False, complete=False, short_circuit=True - ) - mult.init(secp128r1, base) - pt = mult.multiply(13613624287328732) - self.assertIsInstance(pt.coords["X"], Mod) - self.assertIsInstance(pt.coords["Y"], Mod) - self.assertIsInstance(pt.coords["Z"], Mod) - mult.init(secp128r1, pt) - a = mult.multiply(1) - self.assertNotIsInstance(a.coords["X"].x, float) - self.assertNotIsInstance(a.coords["Y"].x, float) - self.assertNotIsInstance(a.coords["Z"].x, float) +def test_issue_7(): + secp128r1 = get_params("secg", "secp128r1", "projective") + base = secp128r1.generator + coords = secp128r1.curve.coordinate_model + add = cast(AdditionFormula, coords.formulas["add-1998-cmo"]) + dbl = cast(DoublingFormula, coords.formulas["dbl-1998-cmo"]) + scl = cast(ScalingFormula, coords.formulas["z"]) + mult = LTRMultiplier(add, dbl, scl, always=False, complete=False, short_circuit=True) + mult.init(secp128r1, base) + pt = mult.multiply(13613624287328732) + assert isinstance(pt.coords["X"], Mod) + assert isinstance(pt.coords["Y"], Mod) + assert isinstance(pt.coords["Z"], Mod) + mult.init(secp128r1, pt) + a = mult.multiply(1) + assert not isinstance(a.coords["X"].x, float) + assert not isinstance(a.coords["Y"].x, float) + assert not isinstance(a.coords["Z"].x, float) + + +def test_issue_8(): + e222 = get_params("other", "E-222", "projective") + base = e222.generator + affine_base = base.to_affine() + affine_double = e222.curve.affine_double(affine_base) + affine_triple = e222.curve.affine_add(affine_base, affine_double) + assert affine_double is not None + assert affine_triple is not None + + +def test_issue_9(): + model = MontgomeryModel() + coords = model.coordinates["xz"] + p = 19 + neutral = Point(coords, X=Mod(1, p), Z=Mod(0, p)) + curve = EllipticCurve(model, coords, p, neutral, {"a": Mod(8, p), "b": Mod(1, p)}) + base = Point(coords, X=Mod(12, p), Z=Mod(1, p)) + formula = coords.formulas["dbl-1987-m-2"] + res = formula(p, base, **curve.parameters)[0] + assert res is not None + affine_base = Point(AffineCoordinateModel(model), x=Mod(12, p), y=Mod(2, p)) + dbase = curve.affine_double(affine_base).to_model(coords, curve) + ladder = coords.formulas["ladd-1987-m-3"] + one, other = ladder(p, base, dbase, base, **curve.parameters) + assert one is not None + assert other is not None + - def test_issue_8(self): - e222 = get_params("other", "E-222", "projective") - base = e222.generator - affine_base = base.to_affine() - affine_double = e222.curve.affine_double(affine_base) - affine_triple = e222.curve.affine_add(affine_base, affine_double) - self.assertIsNotNone(affine_double) - self.assertIsNotNone(affine_triple) +def test_issue_10(): + model = EdwardsModel() + coords = model.coordinates["yz"] + coords_sqr = model.coordinates["yzsquared"] + p = 0x1D + c = Mod(1, p) + d = Mod(0x1C, p) + r = d.sqrt() + neutral = Point(coords, Y=c * r, Z=Mod(1, p)) + curve = EllipticCurve(model, coords, p, neutral, {"c": c, "d": d, "r": r}) + neutral_affine = Point(AffineCoordinateModel(model), x=Mod(0, p), y=c) + assert neutral == neutral_affine.to_model(coords, curve) + neutral_sqr = Point(coords_sqr, Y=c ** 2 * r, Z=Mod(1, p)) + assert neutral_sqr == neutral_affine.to_model(coords_sqr, curve) - def test_issue_9(self): - model = MontgomeryModel() - coords = model.coordinates["xz"] - p = 19 - neutral = Point(coords, X=Mod(1, p), Z=Mod(0, p)) - curve = EllipticCurve( - model, coords, p, neutral, {"a": Mod(8, p), "b": Mod(1, p)} - ) - base = Point(coords, X=Mod(12, p), Z=Mod(1, p)) - formula = coords.formulas["dbl-1987-m-2"] - res = formula(p, base, **curve.parameters)[0] - self.assertIsNotNone(res) - affine_base = Point(AffineCoordinateModel(model), x=Mod(12, p), y=Mod(2, p)) - dbase = curve.affine_double(affine_base).to_model(coords, curve) - ladder = coords.formulas["ladd-1987-m-3"] - one, other = ladder(p, base, dbase, base, **curve.parameters) - self.assertIsNotNone(one) - self.assertIsNotNone(other) - def test_issue_10(self): - model = EdwardsModel() - coords = model.coordinates["yz"] - coords_sqr = model.coordinates["yzsquared"] - p = 0x1D - c = Mod(1, p) - d = Mod(0x1C, p) - r = d.sqrt() - neutral = Point(coords, Y=c * r, Z=Mod(1, p)) - curve = EllipticCurve(model, coords, p, neutral, {"c": c, "d": d, "r": r}) - neutral_affine = Point(AffineCoordinateModel(model), x=Mod(0, p), y=c) - self.assertEqual(neutral, neutral_affine.to_model(coords, curve)) - neutral_sqr = Point(coords_sqr, Y=c ** 2 * r, Z=Mod(1, p)) - self.assertEqual(neutral_sqr, neutral_affine.to_model(coords_sqr, curve)) +def test_issue_13(): + model = EdwardsModel() + coords = model.coordinates["yz"] + c, r, d = symbols("c r d") + p = 53 + c = SymbolicMod(c, p) + r = SymbolicMod(r, p) + d = SymbolicMod(d, p) + yd, zd, yp, zp, yq, zq = symbols("yd zd yp zp yq zq") + PmQ = Point(coords, Y=SymbolicMod(yd, p), Z=SymbolicMod(zd, p)) + P = Point(coords, Y=SymbolicMod(yp, p), Z=SymbolicMod(zp, p)) + Q = Point(coords, Y=SymbolicMod(yq, p), Z=SymbolicMod(zq, p)) + formula = coords.formulas["dadd-2006-g-2"] + formula(p, PmQ, P, Q, c=c, r=r, d=d) - def test_issue_13(self): - model = EdwardsModel() - coords = model.coordinates["yz"] - c, r, d = symbols("c r d") - p = 53 - c = SymbolicMod(c, p) - r = SymbolicMod(r, p) - d = SymbolicMod(d, p) - yd, zd, yp, zp, yq, zq = symbols("yd zd yp zp yq zq") - PmQ = Point(coords, Y=SymbolicMod(yd, p), Z=SymbolicMod(zd, p)) - P = Point(coords, Y=SymbolicMod(yp, p), Z=SymbolicMod(zp, p)) - Q = Point(coords, Y=SymbolicMod(yq, p), Z=SymbolicMod(zq, p)) - formula = coords.formulas["dadd-2006-g-2"] - formula(p, PmQ, P, Q, c=c, r=r, d=d) - @skip("Unresolved issue currently.") - def test_issue_14(self): - model = EdwardsModel() - coords = model.coordinates["projective"] - affine = AffineCoordinateModel(model) - formula = coords.formulas["add-2007-bl-4"] - p = 19 - c = Mod(2, p) - d = Mod(10, p) - curve = EllipticCurve(model, coords, p, InfinityPoint(coords), {"c": c, "d": d}) - Paff = Point(affine, x=Mod(0xD, p), y=Mod(0x9, p)) - P = Paff.to_model(coords, curve) - Qaff = Point(affine, x=Mod(0x4, p), y=Mod(0x12, p)) - Q = Qaff.to_model(coords, curve) - PQaff = curve.affine_add(Paff, Qaff) - R = formula(p, P, Q, **curve.parameters)[0] - Raff = R.to_affine() - self.assertEqual(PQaff, Raff) +@pytest.mark.xfail(reason="Unresolved issue currently.") +def test_issue_14(): + model = EdwardsModel() + coords = model.coordinates["projective"] + affine = AffineCoordinateModel(model) + formula = coords.formulas["add-2007-bl-4"] + p = 19 + c = Mod(2, p) + d = Mod(10, p) + curve = EllipticCurve(model, coords, p, InfinityPoint(coords), {"c": c, "d": d}) + Paff = Point(affine, x=Mod(0xD, p), y=Mod(0x9, p)) + P = Paff.to_model(coords, curve) + Qaff = Point(affine, x=Mod(0x4, p), y=Mod(0x12, p)) + Q = Qaff.to_model(coords, curve) + PQaff = curve.affine_add(Paff, Qaff) + R = formula(p, P, Q, **curve.parameters)[0] + Raff = R.to_affine() + assert PQaff == Raff diff --git a/test/ec/test_signature.py b/test/ec/test_signature.py index f6ab302..73e4611 100644 --- a/test/ec/test_signature.py +++ b/test/ec/test_signature.py @@ -1,7 +1,7 @@ from unittest import TestCase from parameterized import parameterized - +import pytest from pyecsca.ec.params import get_params from pyecsca.ec.mod import Mod from pyecsca.ec.mult import LTRMultiplier @@ -17,81 +17,98 @@ from pyecsca.ec.signature import ( ) -class SignatureTests(TestCase): - def setUp(self): - self.secp128r1 = get_params("secg", "secp128r1", "projective") - self.add = self.secp128r1.curve.coordinate_model.formulas["add-2007-bl"] - self.dbl = self.secp128r1.curve.coordinate_model.formulas["dbl-2007-bl"] - self.mult = LTRMultiplier(self.add, self.dbl) - self.msg = 0xCAFEBABE .to_bytes(4, byteorder="big") - self.priv = Mod(0xDEADBEEF, self.secp128r1.order) - self.mult.init(self.secp128r1, self.secp128r1.generator) - self.pub = self.mult.multiply(self.priv.x) - - @parameterized.expand( - [ - ("SHA1", ECDSA_SHA1), - ("SHA224", ECDSA_SHA224), - ("SHA256", ECDSA_SHA256), - ("SHA384", ECDSA_SHA384), - ("SHA512", ECDSA_SHA512), - ] +@pytest.fixture() +def add(secp128r1): + return secp128r1.curve.coordinate_model.formulas["add-2007-bl"] + + +@pytest.fixture() +def mult(secp128r1, add): + dbl = secp128r1.curve.coordinate_model.formulas["dbl-2007-bl"] + return LTRMultiplier(add, dbl) + + +@pytest.fixture() +def keypair(secp128r1, mult): + priv = Mod(0xDEADBEEF, secp128r1.order) + mult.init(secp128r1, secp128r1.generator) + pub = mult.multiply(int(priv)) + return priv, pub + + +@pytest.fixture() +def msg(): + return 0xCAFEBABE.to_bytes(4, byteorder="big") + + +@pytest.mark.parametrize("name,algo", + [ + ("SHA1", ECDSA_SHA1), + ("SHA224", ECDSA_SHA224), + ("SHA256", ECDSA_SHA256), + ("SHA384", ECDSA_SHA384), + ("SHA512", ECDSA_SHA512), + ]) +def test_all(secp128r1, mult, keypair, msg, add, name, algo): + priv, pub = keypair + signer = algo(mult, secp128r1, privkey=keypair[0]) + assert signer.can_sign + sig = signer.sign_data(msg) + verifier = algo(mult, secp128r1, add=add, pubkey=pub) + assert verifier.can_verify + assert verifier.verify_data(sig, msg) + + none = ECDSA_NONE( + mult, secp128r1, add=add, pubkey=pub, privkey=priv ) - def test_all(self, name, algo): - signer = algo(self.mult, self.secp128r1, privkey=self.priv) - self.assertTrue(signer.can_sign) - sig = signer.sign_data(self.msg) - verifier = algo(self.mult, self.secp128r1, add=self.add, pubkey=self.pub) - self.assertTrue(verifier.can_verify) - self.assertTrue(verifier.verify_data(sig, self.msg)) - - none = ECDSA_NONE( - self.mult, self.secp128r1, add=self.add, pubkey=self.pub, privkey=self.priv - ) - digest = signer.hash_algo(self.msg).digest() - sig = none.sign_hash(digest) - self.assertTrue(none.verify_hash(sig, digest)) - - def test_cannot(self): - ok = ECDSA_NONE( - self.mult, self.secp128r1, add=self.add, pubkey=self.pub, privkey=self.priv - ) - data = b"aaaa" - sig = ok.sign_data(data) - - no_priv = ECDSA_NONE(self.mult, self.secp128r1, pubkey=self.pub) - with self.assertRaises(RuntimeError): - no_priv.sign_data(data) - with self.assertRaises(RuntimeError): - no_priv.sign_hash(data) - no_pubadd = ECDSA_NONE(self.mult, self.secp128r1, privkey=self.priv) - with self.assertRaises(RuntimeError): - no_pubadd.verify_data(sig, data) - with self.assertRaises(RuntimeError): - no_pubadd.verify_hash(sig, data) - - with self.assertRaises(ValueError): - Signature(self.mult, self.secp128r1) - - @parameterized.expand( - [ - ("SHA1", ECDSA_SHA1), - ("SHA224", ECDSA_SHA224), - ("SHA256", ECDSA_SHA256), - ("SHA384", ECDSA_SHA384), - ("SHA512", ECDSA_SHA512), - ] + digest = signer.hash_algo(msg).digest() + sig = none.sign_hash(digest) + assert none.verify_hash(sig, digest) + + +def test_cannot(secp128r1, add, mult, keypair): + priv, pub = keypair + ok = ECDSA_NONE( + mult, secp128r1, add=add, pubkey=pub, privkey=priv ) - def test_fixed_nonce(self, name, algo): - signer = algo(self.mult, self.secp128r1, privkey=self.priv) - sig_one = signer.sign_data(self.msg, nonce=0xABCDEF) - sig_other = signer.sign_data(self.msg, nonce=0xABCDEF) - verifier = algo(self.mult, self.secp128r1, add=self.add, pubkey=self.pub) - self.assertTrue(verifier.verify_data(sig_one, self.msg)) - self.assertTrue(verifier.verify_data(sig_other, self.msg)) - self.assertEqual(sig_one, sig_other) - - def test_der(self): - sig = SignatureResult(0xAAAAA, 0xBBBBB) - self.assertEqual(sig, SignatureResult.from_DER(sig.to_DER())) - self.assertNotEqual(sig, "abc") + data = b"aaaa" + sig = ok.sign_data(data) + + no_priv = ECDSA_NONE(mult, secp128r1, pubkey=pub) + with pytest.raises(RuntimeError): + no_priv.sign_data(data) + with pytest.raises(RuntimeError): + no_priv.sign_hash(data) + no_pubadd = ECDSA_NONE(mult, secp128r1, privkey=priv) + with pytest.raises(RuntimeError): + no_pubadd.verify_data(sig, data) + with pytest.raises(RuntimeError): + no_pubadd.verify_hash(sig, data) + + with pytest.raises(ValueError): + Signature(mult, secp128r1) + + +@pytest.mark.parametrize("name,algo", + [ + ("SHA1", ECDSA_SHA1), + ("SHA224", ECDSA_SHA224), + ("SHA256", ECDSA_SHA256), + ("SHA384", ECDSA_SHA384), + ("SHA512", ECDSA_SHA512), + ]) +def test_fixed_nonce(secp128r1, mult, keypair, msg, add, name, algo): + priv, pub = keypair + signer = algo(mult, secp128r1, privkey=priv) + sig_one = signer.sign_data(msg, nonce=0xABCDEF) + sig_other = signer.sign_data(msg, nonce=0xABCDEF) + verifier = algo(mult, secp128r1, add=add, pubkey=pub) + assert verifier.verify_data(sig_one, msg) + assert verifier.verify_data(sig_other, msg) + assert sig_one == sig_other + + +def test_der(): + sig = SignatureResult(0xAAAAA, 0xBBBBB) + assert sig == SignatureResult.from_DER(sig.to_DER()) + assert sig != "abc" diff --git a/test/ec/test_transformations.py b/test/ec/test_transformations.py index b15f868..f33c8f1 100644 --- a/test/ec/test_transformations.py +++ b/test/ec/test_transformations.py @@ -1,35 +1,34 @@ -from unittest import TestCase - from pyecsca.ec.params import get_params from pyecsca.ec.transformations import M2SW, M2TE, TE2M, SW2M, SW2TE -class TransformationTests(TestCase): - def test_montgomery(self): - curve25519 = get_params("other", "Curve25519", "affine") - sw = M2SW(curve25519) - self.assertIsNotNone(sw) - self.assertTrue(sw.curve.is_on_curve(sw.generator)) - self.assertTrue(sw.curve.is_neutral(sw.curve.neutral)) - te = M2TE(curve25519) - self.assertIsNotNone(te) - self.assertTrue(te.curve.is_on_curve(te.generator)) - self.assertTrue(te.curve.is_neutral(te.curve.neutral)) +def test_montgomery(): + curve25519 = get_params("other", "Curve25519", "affine") + sw = M2SW(curve25519) + assert sw is not None + assert sw.curve.is_on_curve(sw.generator) + assert sw.curve.is_neutral(sw.curve.neutral) + te = M2TE(curve25519) + assert te is not None + assert te.curve.is_on_curve(te.generator) + assert te.curve.is_neutral(te.curve.neutral) + + +def test_twistededwards(): + ed25519 = get_params("other", "Ed25519", "affine") + m = TE2M(ed25519) + assert m is not None + assert m.curve.is_on_curve(m.generator) + assert m.curve.is_neutral(m.curve.neutral) - def test_twistededwards(self): - ed25519 = get_params("other", "Ed25519", "affine") - m = TE2M(ed25519) - self.assertIsNotNone(m) - self.assertTrue(m.curve.is_on_curve(m.generator)) - self.assertTrue(m.curve.is_neutral(m.curve.neutral)) - def test_shortweierstrass(self): - secp128r2 = get_params("secg", "secp128r2", "affine") - m = SW2M(secp128r2) - self.assertIsNotNone(m) - self.assertTrue(m.curve.is_on_curve(m.generator)) - self.assertTrue(m.curve.is_neutral(m.curve.neutral)) - te = SW2TE(secp128r2) - self.assertIsNotNone(te) - self.assertTrue(te.curve.is_on_curve(te.generator)) - self.assertTrue(te.curve.is_neutral(te.curve.neutral)) +def test_shortweierstrass(): + secp128r2 = get_params("secg", "secp128r2", "affine") + m = SW2M(secp128r2) + assert m is not None + assert m.curve.is_on_curve(m.generator) + assert m.curve.is_neutral(m.curve.neutral) + te = SW2TE(secp128r2) + assert te is not None + assert te.curve.is_on_curve(te.generator) + assert te.curve.is_neutral(te.curve.neutral) diff --git a/test/ec/utils.py b/test/ec/utils.py index 0d0a875..1f32033 100644 --- a/test/ec/utils.py +++ b/test/ec/utils.py @@ -1,6 +1,7 @@ from itertools import product from functools import reduce + def cartesian(*items): for cart in product(*items): yield reduce(lambda x, y: x + y, cart) -- cgit v1.2.3-70-g09d2