aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJ08nY2023-08-03 18:44:06 +0200
committerJ08nY2023-08-03 18:44:06 +0200
commit1a98db475e5ecd757411b368dd65f8f894665178 (patch)
treedc592cb72e41ef41dd351ea16682544465a61a8a
parent14a27ac5cb34ec16547e9a1c689713dc24c94a78 (diff)
downloadpyecsca-1a98db475e5ecd757411b368dd65f8f894665178.tar.gz
pyecsca-1a98db475e5ecd757411b368dd65f8f894665178.tar.zst
pyecsca-1a98db475e5ecd757411b368dd65f8f894665178.zip
-rw-r--r--pyecsca/ec/params.py155
-rw-r--r--pyecsca/sca/re/rpa.py2
-rw-r--r--test/data/ecgen_secp128r1.json24
-rw-r--r--test/data/ectester_secp128r1.csv1
-rw-r--r--test/ec/test_params.py17
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)