aboutsummaryrefslogtreecommitdiff
path: root/pyecsca/ec/params.py
blob: 7cf36a3e7cbfc034b6336091e99db5ae5aa09035 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import json
from os.path import join
from typing import Optional, Dict, Union

from pkg_resources import resource_listdir, resource_isdir, resource_stream
from public import public

from .coordinates import AffineCoordinateModel
from .curve import EllipticCurve
from .mod import Mod
from .model import (CurveModel, ShortWeierstrassModel, MontgomeryModel, EdwardsModel,
                    TwistedEdwardsModel)
from .point import Point, InfinityPoint


@public
class DomainParameters(object):
    """Domain parameters which specify a subgroup on an elliptic curve."""
    curve: EllipticCurve
    generator: Point
    order: int
    cofactor: int
    name: Optional[str]
    category: Optional[str]

    def __init__(self, curve: EllipticCurve, generator: Point, order: int,
                 cofactor: int, name: Optional[str] = None, category: Optional[str] = None):
        self.curve = curve
        self.generator = generator
        self.order = order
        self.cofactor = cofactor
        self.name = name
        self.category = category

    def __eq__(self, other):
        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

    def __get_name(self):
        if self.name and self.category:
            return f"{self.category}/{self.name}"
        elif self.name:
            return self.name
        elif self.category:
            return self.category
        return ""

    def __str__(self):
        name = self.__get_name()
        if not name:
            name = str(self.curve)
        return f"{self.__class__.__name__}({name})"

    def __repr__(self):
        return f"{self.__class__.__name__}({self.curve!r}, {self.generator!r}, {self.order}, {self.cofactor})"


@public
def get_params(category: str, name: str, coords: str, infty: bool = True) -> DomainParameters:
    """
    Retrieve a curve from a set of stored parameters. Uses the std-curves database at
    https://github.com/J08nY/std-curves.

    :param category: The category of the curve.
    :param name: The name of the curve.
    :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.
    """
    listing = resource_listdir(__name__, "std")
    categories = list(entry for entry in listing if resource_isdir(__name__, join("std", entry)))
    if category not in categories:
        raise ValueError("Category {} not found.".format(category))
    json_path = join("std", category, "curves.json")
    with resource_stream(__name__, json_path) as f:
        category_json = json.load(f)
    for curve in category_json["curves"]:
        if curve["name"] == name:
            break
    else:
        raise ValueError("Curve {} not found in category {}.".format(name, category))
    if curve["field"]["type"] == "Binary":
        raise ValueError("Binary field curves are currently not supported.")

    # Get model and param names
    model: CurveModel
    field = int(curve["field"]["p"], 16)
    order = int(curve["order"], 16)
    cofactor = int(curve["cofactor"], 16)
    if curve["form"] == "Weierstrass":
        model = ShortWeierstrassModel()
        param_names = ["a", "b"]
    elif curve["form"] == "Montgomery":
        model = MontgomeryModel()
        param_names = ["a", "b"]
    elif curve["form"] == "Edwards":
        model = EdwardsModel()
        param_names = ["c", "d"]
    elif curve["form"] == "TwistedEdwards":
        model = TwistedEdwardsModel()
        param_names = ["a", "d"]
    else:
        raise ValueError("Unknown curve model.")

    # Check coordinate model name and assumptions
    if coords not in model.coordinates:
        raise ValueError("Coordinate model not supported for curve.")
    coord_model = model.coordinates[coords]
    params = {name: Mod(int(curve["params"][name], 16), field) for name in param_names}
    for assumption in coord_model.assumptions:
        alocals: Dict[str, Union[Mod, int]] = {}
        compiled = compile(assumption, "", mode="exec")
        exec(compiled, None, alocals)
        for param, value in alocals.items():
            if params[param] != value:
                raise ValueError(f"Coordinate model {coord_model} has an unsatisifed assumption on the {param} parameter (= {value}).")
    # Construct the point at infinity
    infinity: Point
    if infty:
        infinity = InfinityPoint(coord_model)
    else:
        ilocals: Dict[str, Union[Mod, int]] = {**params}
        for line in coord_model.neutral:
            compiled = compile(line, "", mode="exec")
            exec(compiled, None, ilocals)
        infinity_coords = {}
        for coordinate in coord_model.variables:
            if coordinate not in ilocals:
                raise ValueError(f"Coordinate model {coord_model} requires infty option.")
            value = ilocals[coordinate]
            if isinstance(value, int):
                value = Mod(value, field)
            infinity_coords[coordinate] = value
        infinity = Point(coord_model, **infinity_coords)
    elliptic_curve = EllipticCurve(model, coord_model, field, infinity, params)
    affine = Point(AffineCoordinateModel(model), x=Mod(int(curve["generator"]["x"], 16), field),
                   y=Mod(int(curve["generator"]["y"], 16), field))
    generator = Point.from_affine(coord_model, affine)
    return DomainParameters(elliptic_curve, generator, order, cofactor, name, category)