diff options
| -rw-r--r-- | pyecsca/ec/params.py | 155 | ||||
| -rw-r--r-- | pyecsca/sca/re/rpa.py | 2 | ||||
| -rw-r--r-- | test/data/ecgen_secp128r1.json | 24 | ||||
| -rw-r--r-- | test/data/ectester_secp128r1.csv | 1 | ||||
| -rw-r--r-- | test/ec/test_params.py | 17 |
5 files changed, 173 insertions, 26 deletions
diff --git a/pyecsca/ec/params.py b/pyecsca/ec/params.py index 8344901..82a42bd 100644 --- a/pyecsca/ec/params.py +++ b/pyecsca/ec/params.py @@ -4,6 +4,7 @@ Provides functions for obtaining domain parameters from the `std-curves <https:/ It also provides a domain parameter class and a class for a whole category of domain parameters. """ import json +import csv from sympy import Poly, FF, symbols, sympify from astunparse import unparse from io import RawIOBase, BufferedIOBase @@ -41,13 +42,13 @@ class DomainParameters: category: Optional[str] def __init__( - self, - curve: EllipticCurve, - generator: Point, - order: int, - cofactor: int, - name: Optional[str] = None, - category: Optional[str] = None, + self, + curve: EllipticCurve, + generator: Point, + order: int, + cofactor: int, + name: Optional[str] = None, + category: Optional[str] = None, ): self.curve = curve self.generator = generator @@ -60,10 +61,10 @@ class DomainParameters: if not isinstance(other, DomainParameters): return False return ( - self.curve == other.curve - and self.generator == other.generator - and self.order == other.order - and self.cofactor == other.cofactor + self.curve == other.curve + and self.generator == other.generator + and self.order == other.order + and self.cofactor == other.cofactor ) def __hash__(self): @@ -176,9 +177,9 @@ def _create_params(curve, coords, infty): for curve_param, value in params.items(): expr = expr.subs(curve_param, k(value)) if ( - len(expr.free_symbols) > 1 - or (param := str(expr.free_symbols.pop())) - not in coord_model.parameters + len(expr.free_symbols) > 1 + or (param := str(expr.free_symbols.pop())) + not in coord_model.parameters ): raise ValueError( f"This coordinate model couldn't be loaded due to an unsupported assumption ({assumption_string})." @@ -230,9 +231,9 @@ def _create_params(curve, coords, infty): @public def load_category( - file: Union[str, Path, BinaryIO, IO[bytes]], - coords: Union[str, Callable[[str], str]], - infty: Union[bool, Callable[[str], bool]] = True, + file: Union[str, Path, BinaryIO, IO[bytes]], + coords: Union[str, Callable[[str], str]], + infty: Union[bool, Callable[[str], bool]] = True, ) -> DomainParameterCategory: """ Load a category of domain parameters containing several curves from a JSON file. @@ -268,7 +269,7 @@ def load_category( @public def load_params( - file: Union[str, Path, BinaryIO], coords: str, infty: bool = True + file: Union[str, Path, BinaryIO], coords: str, infty: bool = True ) -> DomainParameters: """ Load a curve from a JSON file. @@ -291,10 +292,120 @@ def load_params( @public +def load_params_ecgen( + file: Union[str, Path, BinaryIO], coords: str, infty: bool = True +) -> DomainParameters: + """ + Load a curve from a file that is output of `ecgen <https://github.com/J08nY/ecgen>`_. + + :param file: The file to load from. + :param coords: The name of the coordinate system to use. + :param infty: Whether to use the special :py:class:InfinityPoint (`True`) or try to use the + point at infinity of the coordinate system. + :return: The curve. + """ + if isinstance(file, (str, Path)): + with open(file, "rb") as f: + ecgen = json.load(f) + elif isinstance(file, (RawIOBase, BufferedIOBase, BinaryIO)): + ecgen = json.load(file) + else: + raise TypeError + ecgen = ecgen[0] + if "m" in ecgen["field"]: + raise ValueError("Binary extension field curves not supported") + if len(ecgen["subgroups"]) != 1: + raise ValueError("Can not represent curve with two subgroups.") + curve_dict = { + "form": "Weierstrass", + "field": { + "type": "Prime", + "p": ecgen["field"]["p"] + }, + "order": ecgen["subgroups"][0]["order"], # Take just the first subgroup + "cofactor": ecgen["subgroups"][0]["cofactor"], + "params": { + "a": { + "raw": ecgen["a"] + }, + "b": { + "raw": ecgen["b"] + } + }, + "generator": { + "x": { + "raw": ecgen["subgroups"][0]["x"] + }, + "y": { + "raw": ecgen["subgroups"][0]["y"] + } + }, + "name": None, + "category": None + } + return _create_params(curve_dict, coords, infty) + + +@public +def load_params_ectester( + file: Union[str, Path, BinaryIO], coords: str, infty: bool = True +) -> DomainParameters: + """ + Load a curve from a file that uses the format of `ECTester <https://github.com/crocs-muni/ECTester>`_. + + :param file: The file to load from. + :param coords: The name of the coordinate system to use. + :param infty: Whether to use the special :py:class:InfinityPoint (`True`) or try to use the + point at infinity of the coordinate system. + :return: The curve. + """ + if isinstance(file, (str, Path)): + with open(file, "r") as f: + reader = csv.reader(f) + line = next(iter(reader)) + elif isinstance(file, (RawIOBase, BufferedIOBase, BinaryIO)): + reader = csv.reader(list(map(lambda line: line.decode(), file.readlines()))) + line = next(iter(reader)) + else: + raise TypeError + if len(line) != 7: + raise ValueError("Binary extension field curves not supported") + # line = p,a,b,gx,gy,n,h (all in hex) + curve_dict = { + "form": "Weierstrass", + "field": { + "type": "Prime", + "p": line[0] + }, + "order": line[5], + "cofactor": line[6], + "params": { + "a": { + "raw": line[1] + }, + "b": { + "raw": line[2] + } + }, + "generator": { + "x": { + "raw": line[3] + }, + "y": { + "raw": line[4] + } + }, + "name": None, + "category": None + } + return _create_params(curve_dict, coords, infty) + + +@public def get_category( - category: str, - coords: Union[str, Callable[[str], str]], - infty: Union[bool, Callable[[str], bool]] = True, + category: str, + coords: Union[str, Callable[[str], str]], + infty: Union[bool, Callable[[str], bool]] = True, ) -> DomainParameterCategory: """ Retrieve a category from the std-curves database at https://github.com/J08nY/std-curves. @@ -320,7 +431,7 @@ def get_category( @public def get_params( - category: str, name: str, coords: str, infty: bool = True + category: str, name: str, coords: str, infty: bool = True ) -> DomainParameters: """ Retrieve a curve from a set of stored parameters. diff --git a/pyecsca/sca/re/rpa.py b/pyecsca/sca/re/rpa.py index 3f1aee5..212e147 100644 --- a/pyecsca/sca/re/rpa.py +++ b/pyecsca/sca/re/rpa.py @@ -134,7 +134,7 @@ def rpa_distinguish(params: DomainParameters, mults: List[ScalarMultiplier], ora :param params: The domain parameters to use. :param mults: The list of possible multipliers. :param oracle: An oracle that returns `True` when an RPA point is encountered during scalar multiplication of the input by the scalar. - :returns: The list of possible multipliers after distinguishing (ideally just one). + :return: The list of possible multipliers after distinguishing (ideally just one). """ P0 = rpa_point_0y(params) or rpa_point_x0(params) if not P0: diff --git a/test/data/ecgen_secp128r1.json b/test/data/ecgen_secp128r1.json new file mode 100644 index 0000000..a1c4796 --- /dev/null +++ b/test/data/ecgen_secp128r1.json @@ -0,0 +1,24 @@ +[{ + "field": { + "p": "0xfffffffdffffffffffffffffffffffff" + }, + "seed": "0x000e0d4d696e6768756151750cc03a4473d03679", + "a": "0xfffffffdfffffffffffffffffffffffc", + "b": "0xe87579c11079f43dd824993c2cee5ed3", + "order": "0xfffffffe0000000075a30d1b9038a115", + "subgroups": [ + { + "x": "0x161ff7528b899b2d0c28607ca52c5b86", + "y": "0xcf5ac8395bafeb13c02da292dded7a83", + "order": "0xfffffffe0000000075a30d1b9038a115", + "cofactor": "0x1", + "points": [ + { + "x": "0x42d2d24d7c0faa3c89b4568396d603c4", + "y": "0x992617de3aa2dfdf8da948913a69f185", + "order": "0xfffffffe0000000075a30d1b9038a115" + } + ] + } + ] +}] diff --git a/test/data/ectester_secp128r1.csv b/test/data/ectester_secp128r1.csv new file mode 100644 index 0000000..f86a00d --- /dev/null +++ b/test/data/ectester_secp128r1.csv @@ -0,0 +1 @@ +0xfffffffdffffffffffffffffffffffff,0xfffffffdfffffffffffffffffffffffc,0xe87579c11079f43dd824993c2cee5ed3,0x161ff7528b899b2d0c28607ca52c5b86,0xcf5ac8395bafeb13c02da292dded7a83,0xfffffffe0000000075a30d1b9038a115,0x1 diff --git a/test/ec/test_params.py b/test/ec/test_params.py index 833272d..23cac3f 100644 --- a/test/ec/test_params.py +++ b/test/ec/test_params.py @@ -7,7 +7,8 @@ from pyecsca.ec.point import Point, InfinityPoint from pyecsca.misc.cfg import TemporaryConfig from pyecsca.ec.coordinates import AffineCoordinateModel from pyecsca.ec.error import UnsatisfiedAssumptionError -from pyecsca.ec.params import get_params, load_params, load_category, get_category, DomainParameters +from pyecsca.ec.params import get_params, load_params, load_category, get_category, DomainParameters, \ + load_params_ectester, load_params_ecgen from pyecsca.ec.model import ShortWeierstrassModel from pyecsca.ec.curve import EllipticCurve @@ -47,8 +48,8 @@ class DomainParameterTests(TestCase): [ ("anssi", "projective"), ( - "brainpool", - lambda name: "projective" if name.endswith("r1") else "jacobian", + "brainpool", + lambda name: "projective" if name.endswith("r1") else "jacobian", ), ] ) @@ -62,6 +63,16 @@ class DomainParameterTests(TestCase): except NotImplementedError: pass + def test_load_params_ectester(self): + params = load_params_ectester("test/data/ectester_secp128r1.csv", "projective") + assert params.curve.is_on_curve(params.generator) + self.assertEqual(params, self.secp128r1) + + def test_load_params_ecgen(self): + params = load_params_ecgen("test/data/ecgen_secp128r1.json", "projective") + assert params.curve.is_on_curve(params.generator) + self.assertEqual(params, self.secp128r1) + def test_load_category(self): category = load_category("test/data/curves.json", "yz") self.assertEqual(len(category), 1) |
