aboutsummaryrefslogtreecommitdiffhomepage
path: root/pyecsca/ec
diff options
context:
space:
mode:
authorJ08nY2023-08-04 14:37:37 +0200
committerJ08nY2023-08-04 14:37:37 +0200
commite54a414a9dd303dac78e435df9d84fe1bcef4676 (patch)
tree7d3250777e63f26ce328c756c0d4ac0f64e7280a /pyecsca/ec
parent1a98db475e5ecd757411b368dd65f8f894665178 (diff)
downloadpyecsca-e54a414a9dd303dac78e435df9d84fe1bcef4676.tar.gz
pyecsca-e54a414a9dd303dac78e435df9d84fe1bcef4676.tar.zst
pyecsca-e54a414a9dd303dac78e435df9d84fe1bcef4676.zip
Add .to_coords to domain params and curve objects.
Also adds coordinate assumption validation to ec curve constructor.
Diffstat (limited to 'pyecsca/ec')
-rw-r--r--pyecsca/ec/curve.py91
-rw-r--r--pyecsca/ec/divpoly.py4
-rw-r--r--pyecsca/ec/params.py31
3 files changed, 106 insertions, 20 deletions
diff --git a/pyecsca/ec/curve.py b/pyecsca/ec/curve.py
index 8fc2793..18dd9cf 100644
--- a/pyecsca/ec/curve.py
+++ b/pyecsca/ec/curve.py
@@ -1,14 +1,18 @@
"""Provides an elliptic curve class."""
from ast import Module
+from astunparse import unparse
from copy import copy
-from typing import MutableMapping, Union, List, Optional
+from typing import MutableMapping, Union, List, Optional, Dict
from public import public
+from sympy import FF, symbols, Poly, sympify
from .coordinates import CoordinateModel, AffineCoordinateModel
+from .error import raise_unsatisified_assumption
from .mod import Mod
from .model import CurveModel
from .point import Point, InfinityPoint
+from ..misc.cfg import getconfig
@public
@@ -27,21 +31,21 @@ class EllipticCurve:
"""The neutral point on the curve."""
def __init__(
- self,
- model: CurveModel,
- coordinate_model: CoordinateModel,
- prime: int,
- neutral: Point,
- parameters: MutableMapping[str, Union[Mod, int]],
+ self,
+ model: CurveModel,
+ coordinate_model: CoordinateModel,
+ prime: int,
+ neutral: Point,
+ parameters: MutableMapping[str, Union[Mod, int]],
):
if coordinate_model not in model.coordinates.values() and not isinstance(
- coordinate_model, AffineCoordinateModel
+ coordinate_model, AffineCoordinateModel
):
raise ValueError
if (
- set(model.parameter_names)
- .union(coordinate_model.parameters)
- .symmetric_difference(parameters.keys())
+ set(model.parameter_names)
+ .union(coordinate_model.parameters)
+ .symmetric_difference(parameters.keys())
):
raise ValueError
self.model = model
@@ -56,6 +60,48 @@ class EllipticCurve:
value = Mod(value, prime)
self.parameters[name] = value
self.neutral = neutral
+ self.__validate_coord_assumptions()
+
+ def __validate_coord_assumptions(self):
+ for assumption in self.coordinate_model.assumptions:
+ # Try to execute assumption, if it works, check with curve parameters
+ # if it doesn't work, move all over to rhs and construct a sympy polynomial of it
+ # then find roots and take first one for new value for new coordinate parameter.
+ try:
+ alocals: Dict[str, Union[Mod, int]] = {}
+ compiled = compile(assumption, "", mode="exec")
+ exec(compiled, None, alocals)
+ for param, value in alocals.items():
+ if self.parameters[param] != value:
+ raise_unsatisified_assumption(
+ getconfig().ec.unsatisfied_coordinate_assumption_action,
+ f"Coordinate model {self.coordinate_model} has an unsatisifed assumption on the {param} parameter (= {value}).",
+ )
+ except NameError:
+ k = FF(self.prime)
+ assumption_string = unparse(assumption)
+ lhs, rhs = assumption_string.split(" = ")
+ expr = sympify(f"{rhs} - {lhs}")
+ for curve_param, value in self.parameters.items():
+ expr = expr.subs(curve_param, k(value))
+ if (
+ len(expr.free_symbols) > 1
+ or (param := str(expr.free_symbols.pop()))
+ not in self.coordinate_model.parameters
+ ):
+ raise ValueError(
+ f"This coordinate model couldn't be loaded due to an unsupported assumption ({assumption_string})."
+ )
+ poly = Poly(expr, symbols(param), domain=k)
+ roots = poly.ground_roots()
+ for root in roots:
+ self.parameters[param] = Mod(int(root), self.prime)
+ break
+ else:
+ raise_unsatisified_assumption(
+ getconfig().ec.unsatisfied_coordinate_assumption_action,
+ f"Coordinate model {self.coordinate_model} has an unsatisifed assumption on the {param} parameter (0 = {expr})."
+ )
def _execute_base_formulas(self, formulas: List[Module], *points: Point) -> Point:
for point in points:
@@ -195,6 +241,18 @@ class EllipticCurve:
loc = {**self.parameters, **point.to_affine().coords}
return eval(compile(self.model.equation, "", mode="eval"), loc)
+ def to_coords(self, coordinate_model: CoordinateModel) -> "EllipticCurve":
+ """
+ Convert this curve into a different coordinate model, only possible if it is currently affine.
+
+ :param coordinate_model: The target coordinate model.
+ :return: The transformed elliptic curve.
+ """
+ if not isinstance(self.coordinate_model, AffineCoordinateModel):
+ raise ValueError
+ return EllipticCurve(self.model, coordinate_model, self.prime, self.neutral.to_model(coordinate_model, self),
+ self.parameters) # type: ignore[arg-type]
+
def to_affine(self) -> "EllipticCurve":
"""
Convert this curve into the affine coordinate model, if possible.
@@ -202,7 +260,8 @@ class EllipticCurve:
:return: The transformed elliptic curve.
"""
coord_model = AffineCoordinateModel(self.model)
- return EllipticCurve(self.model, coord_model, self.prime, self.neutral.to_affine(), self.parameters) # type: ignore[arg-type]
+ return EllipticCurve(self.model, coord_model, self.prime, self.neutral.to_affine(),
+ self.parameters) # type: ignore[arg-type]
def decode_point(self, encoded: bytes) -> Point:
"""
@@ -270,10 +329,10 @@ class EllipticCurve:
if not isinstance(other, EllipticCurve):
return False
return (
- self.model == other.model
- and self.coordinate_model == other.coordinate_model
- and self.prime == other.prime
- and self.parameters == other.parameters
+ self.model == other.model
+ and self.coordinate_model == other.coordinate_model
+ and self.prime == other.prime
+ and self.parameters == other.parameters
)
def __hash__(self):
diff --git a/pyecsca/ec/divpoly.py b/pyecsca/ec/divpoly.py
index c92ef64..a616801 100644
--- a/pyecsca/ec/divpoly.py
+++ b/pyecsca/ec/divpoly.py
@@ -194,6 +194,8 @@ def divpoly(curve: EllipticCurve, n: int, two_torsion_multiplicity: int = 2) ->
return f * divpoly0(curve, -1)[-1]
else:
return f
+ else:
+ raise ValueError
def mult_by_n(curve: EllipticCurve, n: int) -> Tuple[Tuple[Poly, Poly], Tuple[Poly, Poly]]:
@@ -211,7 +213,7 @@ def mult_by_n(curve: EllipticCurve, n: int) -> Tuple[Tuple[Poly, Poly], Tuple[Po
Kxy = lambda r: Poly(r, xs, ys, domain=K) # noqa
if n == 1:
- return x, y
+ return (x, Kxy(1)), (y, Kxy(1))
a1, a2, a3, a4, a6 = a_invariants(curve)
diff --git a/pyecsca/ec/params.py b/pyecsca/ec/params.py
index 82a42bd..1b1edda 100644
--- a/pyecsca/ec/params.py
+++ b/pyecsca/ec/params.py
@@ -17,7 +17,7 @@ from public import public
from .coordinates import AffineCoordinateModel, CoordinateModel
from .curve import EllipticCurve
-from .error import UnsatisfiedAssumptionError, raise_unsatisified_assumption
+from .error import raise_unsatisified_assumption
from .mod import Mod
from .model import (
CurveModel,
@@ -70,6 +70,29 @@ class DomainParameters:
def __hash__(self):
return hash((self.curve, self.generator, self.order, self.cofactor))
+ def to_coords(self, coordinate_model: CoordinateModel) -> "DomainParameters":
+ """
+ Convert the domain parameters into a different coordinate model, only possible if they are currently affine.
+
+ :param coordinate_model: The target coordinate model.
+ :return: The transformed domain parameters
+ """
+ if not isinstance(self.curve.coordinate_model, AffineCoordinateModel):
+ raise ValueError
+ curve = self.curve.to_coords(coordinate_model)
+ generator = self.generator.to_model(coordinate_model, curve)
+ return DomainParameters(curve, generator, self.order, self.cofactor, self.name, self.category)
+
+ def to_affine(self) -> "DomainParameters":
+ """
+ Convert the domain parameters into the affine coordinate model, if possible.
+
+ :return: The transformed domain parameters
+ """
+ curve = self.curve.to_affine()
+ generator = self.generator.to_affine()
+ return DomainParameters(curve, generator, self.order, self.cofactor, self.name, self.category)
+
def __get_name(self):
if self.name and self.category:
return f"{self.category}/{self.name}"
@@ -153,7 +176,7 @@ def _create_params(curve, coords, infty):
coord_model = AffineCoordinateModel(model)
else:
if coords not in model.coordinates:
- raise ValueError("Coordinate model not supported for curve.")
+ raise ValueError("Coordinate model not supported for curve model.")
coord_model = model.coordinates[coords]
for assumption in coord_model.assumptions:
# Try to execute assumption, if it works, check with curve parameters
@@ -188,9 +211,11 @@ def _create_params(curve, coords, infty):
roots = poly.ground_roots()
for root in roots:
params[param] = Mod(int(root), field)
+ print("here", model, coords, param, root)
break
else:
- raise UnsatisfiedAssumptionError(
+ raise_unsatisified_assumption(
+ getconfig().ec.unsatisfied_coordinate_assumption_action,
f"Coordinate model {coord_model} has an unsatisifed assumption on the {param} parameter (0 = {expr})."
)