diff options
| author | J08nY | 2023-08-09 17:40:16 +0200 |
|---|---|---|
| committer | J08nY | 2023-08-09 17:40:16 +0200 |
| commit | b5801a2acfdd149cdb5b24985992e43afdfb9311 (patch) | |
| tree | d78605557eb64ae6282e9e309380efd01e3f0d9f | |
| parent | f40b69968ad9bbb8300b451640a82e1ac83aa7ba (diff) | |
| parent | 1cdf2090574074e94a44b0b939d0dff24727ddf7 (diff) | |
| download | pyecsca-b5801a2acfdd149cdb5b24985992e43afdfb9311.tar.gz pyecsca-b5801a2acfdd149cdb5b24985992e43afdfb9311.tar.zst pyecsca-b5801a2acfdd149cdb5b24985992e43afdfb9311.zip | |
Add RPA/ZVP/EPA stuff.
Merge branch 'zvp-re'
89 files changed, 6660 insertions, 2993 deletions
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index a947379..b8c4fc2 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -43,7 +43,7 @@ jobs: - name: Install dependencies run: | python -m pip install -U pip setuptools wheel - pip install -e ".[picoscope_sdk, picoscope_alt, chipwhisperer, smartcard, leia, gmp, test, dev]" + pip install -e ".[picoscope_sdk, picoscope_alt, chipwhisperer, smartcard, gmp, test, dev]" - name: Typecheck run: | make typecheck diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d7aed43..cd0f777 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -53,8 +53,8 @@ jobs: - name: Install dependencies run: | python -m pip install -U pip setuptools wheel - if [ $USE_GMP == 1 ]; then pip install -e ".[picoscope_sdk, picoscope_alt, chipwhisperer, smartcard, leia, gmp, test, dev]"; fi - if [ $USE_GMP == 0 ]; then pip install -e ".[picoscope_sdk, picoscope_alt, chipwhisperer, smartcard, leia, test, dev]"; fi + if [ $USE_GMP == 1 ]; then pip install -e ".[picoscope_sdk, picoscope_alt, chipwhisperer, smartcard, gmp, test, dev]"; fi + if [ $USE_GMP == 0 ]; then pip install -e ".[picoscope_sdk, picoscope_alt, chipwhisperer, smartcard, test, dev]"; fi - name: Test run: | make test @@ -1,23 +1,24 @@ -EC_TESTS = ec.test_context ec.test_configuration ec.test_curve ec.test_formula \ -ec.test_params ec.test_key_agreement ec.test_key_generation ec.test_mod ec.test_model \ -ec.test_mult ec.test_naf ec.test_op ec.test_point ec.test_signature ec.test_transformations ec.test_regress +EC_TESTS = test.ec.test_context test.ec.test_configuration test.ec.test_curve test.ec.test_formula \ +test.ec.test_params test.ec.test_key_agreement test.ec.test_key_generation test.ec.test_mod test.ec.test_model \ +test.ec.test_mult test.ec.test_naf test.ec.test_op test.ec.test_point test.ec.test_signature test.ec.test_transformations test.ec.test_regress \ +test.ec.test_divpoly -SCA_TESTS = sca.test_align sca.test_combine sca.test_edit sca.test_filter sca.test_match sca.test_process \ -sca.test_sampling sca.test_target sca.test_test sca.test_trace sca.test_traceset sca.test_plot sca.test_rpa \ -sca.test_stacked_combine sca.test_leakage_models +SCA_TESTS = test.sca.test_align test.sca.test_combine test.sca.test_edit test.sca.test_filter test.sca.test_match test.sca.test_process \ +test.sca.test_sampling test.sca.test_target test.sca.test_test test.sca.test_trace test.sca.test_traceset test.sca.test_plot test.sca.test_rpa \ +test.sca.test_zvp test.sca.test_stacked_combine test.sca.test_leakage_models TESTS = ${EC_TESTS} ${SCA_TESTS} PERF_SCRIPTS = test.ec.perf_mod test.ec.perf_formula test.ec.perf_mult test.sca.perf_combine test: - nose2 -s test -E "not slow and not disabled" -C -v ${TESTS} + pytest -m "not slow" --cov=pyecsca test-plots: - env PYECSCA_TEST_PLOTS=1 nose2 -s test -E "not slow and not disabled" -C -v ${TESTS} + env PYECSCA_TEST_PLOTS=1 pytest -m "not slow" test-all: - nose2 -s test -C -v ${TESTS} + pytest --cov=pyecsca typecheck: mypy --namespace-packages -p pyecsca --ignore-missing-imports --show-error-codes --check-untyped-defs diff --git a/pyecsca/ec/context.py b/pyecsca/ec/context.py index dd609f8..5bc9cdf 100644 --- a/pyecsca/ec/context.py +++ b/pyecsca/ec/context.py @@ -9,8 +9,6 @@ multiplication actions has as its children an ordered list of the individual for A :py:class:`PathContext` works like a :py:class:`DefaultContext` that only traces an action on a particular path in the tree. - -A :py:class:`NullContext` does not trace any actions and is the default context. """ from abc import abstractmethod, ABC from collections import OrderedDict @@ -267,6 +265,4 @@ def local(ctx: Optional[Context] = None) -> ContextManager: :param ctx: If none, current context is copied. :return: A context manager. """ - if ctx is None: - ctx = current return _ContextManager(ctx) diff --git a/pyecsca/ec/coordinates.py b/pyecsca/ec/coordinates.py index b258f2d..10ea400 100644 --- a/pyecsca/ec/coordinates.py +++ b/pyecsca/ec/coordinates.py @@ -1,9 +1,9 @@ """Provides a coordinate model class.""" from ast import parse, Module -from os.path import join +from importlib_resources.abc import Traversable +from importlib_resources import as_file from typing import List, Any, MutableMapping -from pkg_resources import resource_listdir, resource_isdir, resource_stream from public import public from .formula import ( @@ -67,11 +67,11 @@ class AffineCoordinateModel(CoordinateModel): return self.curve_model == other.curve_model def __hash__(self): - return hash(self.curve_model) + hash(self.name) + return hash((self.curve_model, self.name)) class EFDCoordinateModel(CoordinateModel): - def __init__(self, dir_path: str, name: str, curve_model: Any): + def __init__(self, dir_path: Traversable, name: str, curve_model: Any): self.name = name self.curve_model = curve_model self.variables = [] @@ -80,31 +80,32 @@ class EFDCoordinateModel(CoordinateModel): self.assumptions = [] self.neutral = [] self.formulas = {} - for fname in resource_listdir(__name__, dir_path): - file_path = join(dir_path, fname) - if resource_isdir(__name__, file_path): - self.__read_formula_dir(file_path, fname) - else: - self.__read_coordinates_file(file_path) + for entry in dir_path.iterdir(): + with as_file(entry) as file_path: + if entry.is_dir(): + self.__read_formula_dir(file_path, file_path.stem) + else: + self.__read_coordinates_file(file_path) - def __read_formula_dir(self, dir_path, formula_type): - for fname in resource_listdir(__name__, dir_path): - if fname.endswith(".op3"): - continue - formula_types = { - "addition": AdditionEFDFormula, - "doubling": DoublingEFDFormula, - "tripling": TriplingEFDFormula, - "diffadd": DifferentialAdditionEFDFormula, - "ladder": LadderEFDFormula, - "scaling": ScalingEFDFormula, - "negation": NegationEFDFormula, - } - cls = formula_types.get(formula_type, EFDFormula) - self.formulas[fname] = cls(join(dir_path, fname), fname, self) + def __read_formula_dir(self, dir_path: Traversable, formula_type): + for entry in dir_path.iterdir(): + with as_file(entry) as fpath: + if fpath.suffix == ".op3": + continue + formula_types = { + "addition": AdditionEFDFormula, + "doubling": DoublingEFDFormula, + "tripling": TriplingEFDFormula, + "diffadd": DifferentialAdditionEFDFormula, + "ladder": LadderEFDFormula, + "scaling": ScalingEFDFormula, + "negation": NegationEFDFormula, + } + cls = formula_types.get(formula_type, EFDFormula) + self.formulas[fpath.stem] = cls(fpath, fpath.with_suffix(".op3"), fpath.stem, self) - def __read_coordinates_file(self, file_path): - with resource_stream(__name__, file_path) as f: + def __read_coordinates_file(self, file_path: Traversable): + with file_path.open("rb") as f: line = f.readline().decode("ascii").rstrip() while line: if line.startswith("name"): @@ -137,4 +138,4 @@ class EFDCoordinateModel(CoordinateModel): return self.curve_model == other.curve_model and self.name == other.name def __hash__(self): - return hash(self.curve_model) + hash(self.name) + return hash((self.curve_model, self.name)) diff --git a/pyecsca/ec/curve.py b/pyecsca/ec/curve.py index 8fc2793..fb567e8 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, 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,39 @@ 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) > 0: + raise ValueError( + f"Missing necessary coordinate model parameter ({assumption_string})." + ) + if k(expr) != 0: + 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 +232,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 +251,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 +320,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 new file mode 100644 index 0000000..a616801 --- /dev/null +++ b/pyecsca/ec/divpoly.py @@ -0,0 +1,271 @@ +""" +Provides functions for computing division polynomials and the multiplication-by-n map on an elliptic curve. +""" +from typing import Tuple, Dict, Set, Mapping + +from sympy import symbols, FF, Poly +import networkx as nx + +from .curve import EllipticCurve +from .mod import Mod +from .model import ShortWeierstrassModel + + +def values(*ns: int) -> Mapping[int, Tuple[int, ...]]: + done: Set[int] = set() + vals = {} + todo: Set[int] = set() + todo.update(ns) + while todo: + val = todo.pop() + if val in done: + continue + new: Tuple[int, ...] = () + if val == -2: + new = (-1,) + elif val == -1: + pass + elif val < 0: + raise ValueError(f"bad {val}") + elif val in (0, 1, 2, 3): + pass + elif val == 4: + new = (-2, 3) + elif val % 2 == 0: + m = (val - 2) // 2 + new = (m + 1, m + 3, m, m - 1, m + 2) + else: + m = (val - 1) // 2 + if m % 2 == 0: + new = (-2, m + 2, m, m - 1, m + 1) + else: + new = (m + 2, m, -2, m - 1, m + 1) + if new: + todo.update(new) + vals[val] = new + done.add(val) + return vals + + +def dep_graph(*ns: int): + g = nx.DiGraph() + vals = values(*ns) + for k, v in vals.items(): + if v: + for e in v: + g.add_edge(k, e) + else: + g.add_node(k) + return g, vals + + +def dep_map(*ns: int): + g, vals = dep_graph(*ns) + current: Set[int] = set() + ls = [] + for vert in nx.lexicographical_topological_sort(g, key=lambda v: -sum(g[v].keys())): + if vert in current: + current.remove(vert) + ls.append((vert, set(current))) + current.update(vals[vert]) + ls.reverse() + return ls, vals + + +def a_invariants(curve: EllipticCurve) -> Tuple[Mod, ...]: + """ + Compute the a-invariants of the curve. + + :param curve: The elliptic curve (only ShortWeierstrass model). + :return: A tuple of 5 a-invariants (a1, a2, a3, a4, a6). + """ + if isinstance(curve.model, ShortWeierstrassModel): + a1 = Mod(0, curve.prime) + a2 = Mod(0, curve.prime) + a3 = Mod(0, curve.prime) + a4 = curve.parameters["a"] + a6 = curve.parameters["b"] + return a1, a2, a3, a4, a6 + else: + raise NotImplementedError + + +def b_invariants(curve: EllipticCurve) -> Tuple[Mod, ...]: + """ + Compute the b-invariants of the curve. + + :param curve: The elliptic curve (only ShortWeierstrass model). + :return: A tuple of 4 b-invariants (b2, b4, b6, b8). + """ + if isinstance(curve.model, ShortWeierstrassModel): + a1, a2, a3, a4, a6 = a_invariants(curve) + return (a1 * a1 + 4 * a2, + a1 * a3 + 2 * a4, + a3 ** 2 + 4 * a6, + a1 ** 2 * a6 + 4 * a2 * a6 - a1 * a3 * a4 + a2 * a3 ** 2 - a4 ** 2) + else: + raise NotImplementedError + + +def divpoly0(curve: EllipticCurve, *ns: int) -> Mapping[int, Poly]: + """ + Basically sagemath's division_polynomial_0 but more clever memory management. + + As sagemath says: + + Return the `n^{th}` torsion (division) polynomial, without + the 2-torsion factor if `n` is even, as a polynomial in `x`. + + These are the polynomials `g_n` defined in [MT1991]_, but with + the sign flipped for even `n`, so that the leading coefficient is + always positive. + + :param curve: The elliptic curve. + :param ns: The values to compute the polynomial for. + :return: + """ + xs = symbols("x") + + K = FF(curve.prime) + Kx = lambda r: Poly(r, xs, domain=K) # noqa + + x = Kx(xs) + + b2, b4, b6, b8 = map(lambda b: Kx(int(b)), b_invariants(curve)) + ls, vals = dep_map(*ns) + + mem: Dict[int, Poly] = {} + for i, keep in ls: + if i == -2: + val = mem[-1] ** 2 + elif i == -1: + val = Kx(4) * x ** 3 + b2 * x ** 2 + Kx(2) * b4 * x + b6 + elif i == 0: + val = Kx(0) + elif i < 0: + raise ValueError("n must be a positive integer (or -1 or -2)") + elif i == 1 or i == 2: + val = Kx(1) + elif i == 3: + val = Kx(3) * x ** 4 + b2 * x ** 3 + Kx(3) * b4 * x ** 2 + Kx(3) * b6 * x + b8 + elif i == 4: + val = -mem[-2] + (Kx(6) * x ** 2 + b2 * x + b4) * mem[3] + elif i % 2 == 0: + m = (i - 2) // 2 + val = mem[m + 1] * (mem[m + 3] * mem[m] ** 2 - mem[m - 1] * mem[m + 2] ** 2) + else: + m = (i - 1) // 2 + if m % 2 == 0: + val = mem[-2] * mem[m + 2] * mem[m] ** 3 - mem[m - 1] * mem[m + 1] ** 3 + else: + val = mem[m + 2] * mem[m] ** 3 - mem[-2] * mem[m - 1] * mem[m + 1] ** 3 + for dl in set(mem.keys()).difference(keep).difference(ns): + del mem[dl] + mem[i] = val + + return mem + + +def divpoly(curve: EllipticCurve, n: int, two_torsion_multiplicity: int = 2) -> Poly: + """ + Compute the n-th division polynomial. + + :param curve: Curve to compute on. + :param n: Scalar. + :param two_torsion_multiplicity: Same as sagemath. + :return: The division polynomial. + """ + f: Poly = divpoly0(curve, n)[n] + a1, a2, a3, a4, a6 = a_invariants(curve) + xs, ys = symbols("x y") + x = Poly(xs, xs, domain=f.domain) + y = Poly(ys, ys, domain=f.domain) + + if two_torsion_multiplicity == 0: + return f + elif two_torsion_multiplicity == 1: + if n % 2 == 0: + Kxy = lambda r: Poly(r, xs, ys, domain=f.domain) # noqa + return Kxy(f) * (Kxy(2) * y + Kxy(a1) * x + Kxy(a3)) + else: + return f + elif two_torsion_multiplicity == 2: + if n % 2 == 0: + 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]]: + """ + Compute the multiplication-by-n map on an elliptic curve. + + :param curve: Curve to compute on. + :param n: Scalar. + :return: A tuple (mx, my) where each is a tuple (numerator, denominator). + """ + xs, ys = symbols("x y") + K = FF(curve.prime) + x = Poly(xs, xs, domain=K) + y = Poly(ys, ys, domain=K) + Kxy = lambda r: Poly(r, xs, ys, domain=K) # noqa + + if n == 1: + return (x, Kxy(1)), (y, Kxy(1)) + + a1, a2, a3, a4, a6 = a_invariants(curve) + + polys = divpoly0(curve, -2, -1, n - 1, n, n + 1, n + 2) + # TODO: All of these fractions may benefit from using + # sympy.cancel to get rid of common factors in the numerator and denominator. + # Though for large polynomials that might be too much. + mx_denom = polys[n] ** 2 + if n % 2 == 0: + mx_num = x * polys[-1] * polys[n] ** 2 - polys[n - 1] * polys[n + 1] + mx_denom *= polys[-1] + else: + mx_num = x * polys[n] ** 2 - polys[-1] * polys[n - 1] * polys[n + 1] + + # Alternative that makes the denominator monic by dividing the + # numerator by the leading coefficient. Sage does this + # simplification when asking for multiplication_by_m with the + # x-only=True, as then the poly is an univariate object. + # lc = K(mx_denom.LC()) + # mx = (mx_num.quo(lc), mx_denom.monic()) + mx = (mx_num, mx_denom) + + # The following lines compute + # my = ((2*y+a1*x+a3)*mx.derivative(x)/m - a1*mx-a3)/2 + # just as sage does, but using sympy and step-by-step + # tracking the numerator and denominator of the fraction. + + # mx.derivative() + mxd_num = mx[1] * mx[0].diff() - mx[0] * mx[1].diff() + mxd_denom = mx[1] ** 2 + + # mx.derivative()/m + mxd_dn_num = mxd_num + mxd_dn_denom = mxd_denom * Kxy(n) + + # (2*y+a1*x+a3)*mx.derivative(x)/m + mxd_full_num = mxd_dn_num * (Kxy(2) * y + Kxy(a1) * x + Kxy(a3)) + mxd_full_denom = mxd_dn_denom + + # a1*mx + a1mx_num = (Kxy(a1) * mx[0]) + a1mx_denom = mx[1] # noqa + + # a3 + a3_num = (Kxy(a3) * mx[1]) + a3_denom = mx[1] # noqa + + # The mx.derivative part has a different denominator, basically mx[1]^2 * m + # so the rest needs to be multiplied by this factor when subtracitng. + mxd_fact = mx[1] * n + + my_num = (mxd_full_num - a1mx_num * mxd_fact - a3_num * mxd_fact) + my_denom = mxd_full_denom * Kxy(2) + my = (my_num, my_denom) + return mx, my diff --git a/pyecsca/ec/efd b/pyecsca/ec/efd -Subproject a39cbee81c9a5c0ec720abf3eb97cfd4a3d06af +Subproject bfecf9e69ae1b20f0fe1b83496407c2ac09cd72 diff --git a/pyecsca/ec/formula.py b/pyecsca/ec/formula.py index 58978a7..d737c70 100644 --- a/pyecsca/ec/formula.py +++ b/pyecsca/ec/formula.py @@ -7,7 +7,7 @@ from astunparse import unparse from itertools import product from typing import List, Set, Any, ClassVar, MutableMapping, Tuple, Union, Dict -from pkg_resources import resource_stream +from importlib_resources.abc import Traversable from public import public from sympy import sympify, FF, symbols, Poly, Rational @@ -40,6 +40,7 @@ class OpResult: return self.name def __repr__(self): + # TODO: This repr is broken for square and neg and inv. char = self.op.op_str parents = char.join(str(parent) for parent in self.parents) return f"{self.name} = {parents}" @@ -338,7 +339,7 @@ class Formula(ABC): class EFDFormula(Formula): """Formula from the `Explicit-Formulas Database <https://www.hyperelliptic.org/EFD/>`_.""" - def __init__(self, path: str, name: str, coordinate_model: Any): + def __init__(self, meta_path: Traversable, op3_path: Traversable, name: str, coordinate_model: Any): self.name = name self.coordinate_model = coordinate_model self.meta = {} @@ -346,11 +347,11 @@ class EFDFormula(Formula): self.assumptions = [] self.code = [] self.unified = False - self.__read_meta_file(path) - self.__read_op3_file(path + ".op3") + self.__read_meta_file(meta_path) + self.__read_op3_file(op3_path) - def __read_meta_file(self, path): - with resource_stream(__name__, path) as f: + def __read_meta_file(self, path: Traversable): + with path.open("rb") as f: line = f.readline().decode("ascii").rstrip() while line: if line.startswith("source"): @@ -367,11 +368,11 @@ class EFDFormula(Formula): self.unified = True line = f.readline().decode("ascii").rstrip() - def __read_op3_file(self, path): - with resource_stream(__name__, path) as f: + def __read_op3_file(self, path: Traversable): + with path.open("rb") as f: for line in f.readlines(): code_module = parse( - line.decode("ascii").replace("^", "**"), path, mode="exec" + line.decode("ascii").replace("^", "**"), str(path), mode="exec" ) self.code.append(CodeOp(code_module)) @@ -410,7 +411,7 @@ class EFDFormula(Formula): ) def __hash__(self): - return hash(self.name) + hash(self.coordinate_model) + return hash((self.coordinate_model, self.name)) @public diff --git a/pyecsca/ec/mod.py b/pyecsca/ec/mod.py index a974462..3dcb609 100644 --- a/pyecsca/ec/mod.py +++ b/pyecsca/ec/mod.py @@ -10,7 +10,7 @@ dispatches to the implementation chosen by the runtime configuration of the libr import random import secrets from functools import wraps, lru_cache -from typing import Type, Dict, Any, Tuple, Union +from typing import Type, Dict, Any, Tuple, Union, Optional from public import public from sympy import Expr, FF @@ -148,7 +148,7 @@ class Mod: n: Any __slots__ = ("x", "n") - def __new__(cls, *args, **kwargs): + def __new__(cls, *args, **kwargs) -> "Mod": if cls != Mod: return cls.__new__(cls, *args, **kwargs) if not _mod_classes: @@ -561,15 +561,13 @@ class SymbolicMod(Mod): return str(self.x) def __hash__(self): - return hash(("SymbolicMod", self.x, self.n)) + 1 + return hash(("SymbolicMod", self.x, self.n)) def __pow__(self, n) -> "SymbolicMod": - try: - x = pow(self.x, n, self.n) - except TypeError: - x = pow(self.x, n) % self.n - return SymbolicMod(x, self.n) + return self.__class__(pow(self.x, n), self.n) + +_mod_classes["symbolic"] = SymbolicMod if has_gmp: diff --git a/pyecsca/ec/model.py b/pyecsca/ec/model.py index bea5554..7827a51 100644 --- a/pyecsca/ec/model.py +++ b/pyecsca/ec/model.py @@ -1,9 +1,9 @@ """Provides curve model classes for the supported curve models.""" from ast import parse, Expression, Module -from os.path import join from typing import List, MutableMapping +from importlib_resources import files, as_file +from importlib_resources.abc import Traversable -from pkg_resources import resource_listdir, resource_isdir, resource_stream from public import public from .coordinates import EFDCoordinateModel, CoordinateModel @@ -50,19 +50,18 @@ class EFDCurveModel(CurveModel): self.__class__.to_weierstrass = [] self.__class__.from_weierstrass = [] - files = resource_listdir(__name__, join("efd", efd_name)) - for fname in files: - file_path = join("efd", efd_name, fname) - if resource_isdir(__name__, file_path): - self.__read_coordinate_dir(self.__class__, file_path, fname) - else: - self.__read_curve_file(self.__class__, file_path) + for entry in files("pyecsca.ec").joinpath("efd", efd_name).iterdir(): + with as_file(entry) as file_path: + if entry.is_dir(): + self.__read_coordinate_dir(self.__class__, file_path, file_path.stem) + else: + self.__read_curve_file(self.__class__, file_path) - def __read_curve_file(self, cls, file_path): + def __read_curve_file(self, cls, file_path: Traversable): def format_eq(line, mode="exec"): return parse(line.replace("^", "**"), mode=mode) - with resource_stream(__name__, file_path) as f: + with file_path.open("rb") as f: for raw in f.readlines(): line = raw.decode("ascii").rstrip() if line.startswith("name"): @@ -90,7 +89,7 @@ class EFDCurveModel(CurveModel): else: cls.full_weierstrass.append(format_eq(line)) - def __read_coordinate_dir(self, cls, dir_path, name): + def __read_coordinate_dir(self, cls, dir_path: Traversable, name: str): cls.coordinates[name] = EFDCoordinateModel(dir_path, name, self) def __eq__(self, other): @@ -99,7 +98,7 @@ class EFDCurveModel(CurveModel): return self._efd_name == other._efd_name def __hash__(self): - return hash(self._efd_name) + 1 + return hash(self._efd_name) def __str__(self): return f"{self.__class__.__name__.replace('Model', '')}" diff --git a/pyecsca/ec/mult.py b/pyecsca/ec/mult.py index 53e593e..2effb10 100644 --- a/pyecsca/ec/mult.py +++ b/pyecsca/ec/mult.py @@ -48,6 +48,9 @@ class PrecomputationAction(Action): self.params = params self.point = point + def __repr__(self): + return f"{self.__class__.__name__}({self.params}, {self.point})" + @public class ScalarMultiplier(ABC): @@ -73,14 +76,14 @@ class ScalarMultiplier(ABC): def __init__(self, short_circuit: bool = True, **formulas: Optional[Formula]): if ( - len( - { - formula.coordinate_model - for formula in formulas.values() - if formula is not None - } - ) - != 1 + len( + { + formula.coordinate_model + for formula in formulas.values() + if formula is not None + } + ) + != 1 ): raise ValueError self.short_circuit = short_circuit @@ -102,8 +105,8 @@ class ScalarMultiplier(ABC): if "dbl" not in self.formulas: raise NotImplementedError if ( - self.short_circuit - and point == self._params.curve.neutral + self.short_circuit + and point == self._params.curve.neutral ): return copy(point) return self.formulas["dbl"]( @@ -152,6 +155,17 @@ class ScalarMultiplier(ABC): self._params.curve.prime, point, **self._params.curve.parameters )[0] + def __hash__(self): + return id(self) + + def __eq__(self, other): + if not isinstance(other, ScalarMultiplier): + return False + return self.formulas == other.formulas and self.short_circuit == other.short_circuit + + def __repr__(self): + return f"{self.__class__.__name__}({tuple(self.formulas.values())}, short_circuit={self.short_circuit})" + def init(self, params: DomainParameters, point: Point): """ Initialize the scalar multiplier with :paramref:`~.init.params` and a :paramref:`~.init.point`. @@ -164,8 +178,8 @@ class ScalarMultiplier(ABC): """ coord_model = set(self.formulas.values()).pop().coordinate_model if ( - params.curve.coordinate_model != coord_model - or point.coordinate_model != coord_model + params.curve.coordinate_model != coord_model + or point.coordinate_model != coord_model ): raise ValueError self._params = params @@ -200,18 +214,26 @@ class LTRMultiplier(ScalarMultiplier): complete: bool def __init__( - self, - add: AdditionFormula, - dbl: DoublingFormula, - scl: Optional[ScalingFormula] = None, - always: bool = False, - complete: bool = True, - short_circuit: bool = True, + self, + add: AdditionFormula, + dbl: DoublingFormula, + scl: Optional[ScalingFormula] = None, + always: bool = False, + complete: bool = True, + short_circuit: bool = True, ): super().__init__(short_circuit=short_circuit, add=add, dbl=dbl, scl=scl) self.always = always self.complete = complete + def __hash__(self): + return id(self) + + def __eq__(self, other): + if not isinstance(other, LTRMultiplier): + return False + return self.formulas == other.formulas and self.short_circuit == other.short_circuit and self.always == other.always and self.complete == other.complete + def multiply(self, scalar: int) -> Point: if not self._initialized: raise ValueError("ScalarMultiplier not initialized.") @@ -251,16 +273,24 @@ class RTLMultiplier(ScalarMultiplier): always: bool def __init__( - self, - add: AdditionFormula, - dbl: DoublingFormula, - scl: Optional[ScalingFormula] = None, - always: bool = False, - short_circuit: bool = True, + self, + add: AdditionFormula, + dbl: DoublingFormula, + scl: Optional[ScalingFormula] = None, + always: bool = False, + short_circuit: bool = True, ): super().__init__(short_circuit=short_circuit, add=add, dbl=dbl, scl=scl) self.always = always + def __hash__(self): + return id(self) + + def __eq__(self, other): + if not isinstance(other, RTLMultiplier): + return False + return self.formulas == other.formulas and self.short_circuit == other.short_circuit and self.always == other.always + def multiply(self, scalar: int) -> Point: if not self._initialized: raise ValueError("ScalarMultiplier not initialized.") @@ -297,14 +327,22 @@ class CoronMultiplier(ScalarMultiplier): optionals = {ScalingFormula} def __init__( - self, - add: AdditionFormula, - dbl: DoublingFormula, - scl: Optional[ScalingFormula] = None, - short_circuit: bool = True, + self, + add: AdditionFormula, + dbl: DoublingFormula, + scl: Optional[ScalingFormula] = None, + short_circuit: bool = True, ): super().__init__(short_circuit=short_circuit, add=add, dbl=dbl, scl=scl) + def __hash__(self): + return id(self) + + def __eq__(self, other): + if not isinstance(other, CoronMultiplier): + return False + return self.formulas == other.formulas and self.short_circuit == other.short_circuit + def multiply(self, scalar: int) -> Point: if not self._initialized: raise ValueError("ScalarMultiplier not initialized.") @@ -332,18 +370,26 @@ class LadderMultiplier(ScalarMultiplier): complete: bool def __init__( - self, - ladd: LadderFormula, - dbl: Optional[DoublingFormula] = None, - scl: Optional[ScalingFormula] = None, - complete: bool = True, - short_circuit: bool = True, + self, + ladd: LadderFormula, + dbl: Optional[DoublingFormula] = None, + scl: Optional[ScalingFormula] = None, + complete: bool = True, + short_circuit: bool = True, ): super().__init__(short_circuit=short_circuit, ladd=ladd, dbl=dbl, scl=scl) self.complete = complete if (not complete or short_circuit) and dbl is None: raise ValueError + def __hash__(self): + return id(self) + + def __eq__(self, other): + if not isinstance(other, LadderMultiplier): + return False + return self.formulas == other.formulas and self.short_circuit == other.short_circuit and self.complete == other.complete + def multiply(self, scalar: int) -> Point: if not self._initialized: raise ValueError("ScalarMultiplier not initialized.") @@ -378,16 +424,24 @@ class SimpleLadderMultiplier(ScalarMultiplier): complete: bool def __init__( - self, - add: AdditionFormula, - dbl: DoublingFormula, - scl: Optional[ScalingFormula] = None, - complete: bool = True, - short_circuit: bool = True, + self, + add: AdditionFormula, + dbl: DoublingFormula, + scl: Optional[ScalingFormula] = None, + complete: bool = True, + short_circuit: bool = True, ): super().__init__(short_circuit=short_circuit, add=add, dbl=dbl, scl=scl) self.complete = complete + def __hash__(self): + return id(self) + + def __eq__(self, other): + if not isinstance(other, SimpleLadderMultiplier): + return False + return self.formulas == other.formulas and self.short_circuit == other.short_circuit and self.complete == other.complete + def multiply(self, scalar: int) -> Point: if not self._initialized: raise ValueError("ScalarMultiplier not initialized.") @@ -421,16 +475,24 @@ class DifferentialLadderMultiplier(ScalarMultiplier): complete: bool def __init__( - self, - dadd: DifferentialAdditionFormula, - dbl: DoublingFormula, - scl: Optional[ScalingFormula] = None, - complete: bool = True, - short_circuit: bool = True, + self, + dadd: DifferentialAdditionFormula, + dbl: DoublingFormula, + scl: Optional[ScalingFormula] = None, + complete: bool = True, + short_circuit: bool = True, ): super().__init__(short_circuit=short_circuit, dadd=dadd, dbl=dbl, scl=scl) self.complete = complete + def __hash__(self): + return id(self) + + def __eq__(self, other): + if not isinstance(other, DifferentialLadderMultiplier): + return False + return self.formulas == other.formulas and self.short_circuit == other.short_circuit and self.complete == other.complete + def multiply(self, scalar: int) -> Point: if not self._initialized: raise ValueError("ScalarMultiplier not initialized.") @@ -465,17 +527,25 @@ class BinaryNAFMultiplier(ScalarMultiplier): _point_neg: Point def __init__( - self, - add: AdditionFormula, - dbl: DoublingFormula, - neg: NegationFormula, - scl: Optional[ScalingFormula] = None, - short_circuit: bool = True, + self, + add: AdditionFormula, + dbl: DoublingFormula, + neg: NegationFormula, + scl: Optional[ScalingFormula] = None, + short_circuit: bool = True, ): super().__init__( short_circuit=short_circuit, add=add, dbl=dbl, neg=neg, scl=scl ) + def __hash__(self): + return id(self) + + def __eq__(self, other): + if not isinstance(other, BinaryNAFMultiplier): + return False + return self.formulas == other.formulas and self.short_circuit == other.short_circuit + def init(self, params: DomainParameters, point: Point): with PrecomputationAction(params, point): super().init(params, point) @@ -512,14 +582,14 @@ class WindowNAFMultiplier(ScalarMultiplier): width: int def __init__( - self, - add: AdditionFormula, - dbl: DoublingFormula, - neg: NegationFormula, - width: int, - scl: Optional[ScalingFormula] = None, - precompute_negation: bool = False, - short_circuit: bool = True, + self, + add: AdditionFormula, + dbl: DoublingFormula, + neg: NegationFormula, + width: int, + scl: Optional[ScalingFormula] = None, + precompute_negation: bool = False, + short_circuit: bool = True, ): super().__init__( short_circuit=short_circuit, add=add, dbl=dbl, neg=neg, scl=scl @@ -527,6 +597,14 @@ class WindowNAFMultiplier(ScalarMultiplier): self.width = width self.precompute_negation = precompute_negation + def __hash__(self): + return id(self) + + def __eq__(self, other): + if not isinstance(other, WindowNAFMultiplier): + return False + return self.formulas == other.formulas and self.short_circuit == other.short_circuit and self.width == other.width and self.precompute_negation == other.precompute_negation + def init(self, params: DomainParameters, point: Point): with PrecomputationAction(params, point): super().init(params, point) diff --git a/pyecsca/ec/params.py b/pyecsca/ec/params.py index 8344901..4141dfa 100644 --- a/pyecsca/ec/params.py +++ b/pyecsca/ec/params.py @@ -4,19 +4,19 @@ 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 -from os.path import join from pathlib import Path from typing import Optional, Dict, Union, BinaryIO, List, Callable, IO +from importlib_resources import files -from pkg_resources import resource_listdir, resource_isdir, resource_stream 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, @@ -41,13 +41,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,15 +60,38 @@ 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): 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}" @@ -152,7 +175,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 @@ -176,9 +199,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})." @@ -189,7 +212,8 @@ def _create_params(curve, coords, infty): params[param] = Mod(int(root), field) 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})." ) @@ -230,9 +254,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 +292,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 +315,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. @@ -307,20 +441,18 @@ def get_category( as argument the name of the curve and returns the infinity option to use for that curve. :return: The category. """ - listing = resource_listdir(__name__, "std") - categories = [ - entry for entry in listing if resource_isdir(__name__, join("std", entry)) - ] + categories = { + entry.name: entry for entry in files("pyecsca.ec").joinpath("std").iterdir() if entry.is_dir() + } if category not in categories: raise ValueError(f"Category {category} not found.") - json_path = join("std", category, "curves.json") - with resource_stream(__name__, json_path) as f: + with categories[category].joinpath("curves.json").open("rb") as f: return load_category(f, coords, infty) @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. @@ -334,14 +466,12 @@ def get_params( point at infinity of the coordinate system. :return: The curve. """ - listing = resource_listdir(__name__, "std") - categories = [ - entry for entry in listing if resource_isdir(__name__, join("std", entry)) - ] + categories = { + entry.name: entry for entry in files("pyecsca.ec").joinpath("std").iterdir() if entry.is_dir() + } if category not in categories: raise ValueError(f"Category {category} not found.") - json_path = join("std", category, "curves.json") - with resource_stream(__name__, json_path) as f: + with categories[category].joinpath("curves.json").open("rb") as f: category_json = json.load(f) curve = None for curve in category_json["curves"]: diff --git a/pyecsca/ec/point.py b/pyecsca/ec/point.py index c4da538..b63c924 100644 --- a/pyecsca/ec/point.py +++ b/pyecsca/ec/point.py @@ -203,7 +203,7 @@ class Point: return self.coords == other.coords def __hash__(self): - return hash((self.coordinate_model.name, tuple(self.coords.keys()), tuple(self.coords.values()))) + 13 + return hash((self.coordinate_model, tuple(self.coords.keys()), tuple(self.coords.values()))) def __str__(self): args = ", ".join([f"{key}={val}" for key, val in self.coords.items()]) @@ -254,7 +254,7 @@ class InfinityPoint(Point): return self.coordinate_model == other.coordinate_model def __hash__(self): - return hash(self.coordinate_model.name) + 13 + return hash((self.coordinate_model, 0)) def __str__(self): return "Infinity" diff --git a/pyecsca/ec/signature.py b/pyecsca/ec/signature.py index a65adbc..abad783 100644 --- a/pyecsca/ec/signature.py +++ b/pyecsca/ec/signature.py @@ -43,7 +43,7 @@ class SignatureResult: return self.r == other.r and self.s == other.s def __hash__(self): - return hash((self.r, self.s)) + 11 + return hash((self.r, self.s)) def __str__(self): return f"(r={self.r}, s={self.s})" @@ -74,6 +74,7 @@ class ECDSAAction(Action): class ECDSASignAction(ECDSAAction): """ECDSA signing.""" + # TODO: Make this a ResultAction privkey: Mod def __init__( @@ -94,6 +95,7 @@ class ECDSASignAction(ECDSAAction): class ECDSAVerifyAction(ECDSAAction): """ECDSA verification.""" + # TODO: Make this a ResultAction signature: SignatureResult pubkey: Point diff --git a/pyecsca/ec/std b/pyecsca/ec/std -Subproject cd41e851723dfebd76a978717c0d89569c49597 +Subproject 32935e4291eb90c7d86baf082a3c0f0bfb4bcca diff --git a/pyecsca/misc/__init__.py b/pyecsca/misc/__init__.py index f093677..243fc08 100644 --- a/pyecsca/misc/__init__.py +++ b/pyecsca/misc/__init__.py @@ -1 +1 @@ -"""package for miscellaneous things.""" +"""Package for miscellaneous things.""" diff --git a/pyecsca/misc/cfg.py b/pyecsca/misc/cfg.py index 2affb27..ee9c95d 100644 --- a/pyecsca/misc/cfg.py +++ b/pyecsca/misc/cfg.py @@ -111,13 +111,14 @@ class ECConfig: - ``"gmp"``: Requires the GMP library and `gmpy2` package. - ``"python"``: Doesn't require anything. + - ``"symbolic"``: Requires sympy. """ return self._mod_implementation @mod_implementation.setter def mod_implementation(self, value: str): - if value not in ("python", "gmp"): - raise ValueError("Bad Mod implementaiton, can be one of 'python' or 'gmp'.") + if value not in ("python", "gmp", "symbolic"): + raise ValueError("Bad Mod implementaiton, can be one of 'python', 'gmp' or 'symbolic'.") self._mod_implementation = value diff --git a/pyecsca/sca/attack/leakage_model.py b/pyecsca/sca/attack/leakage_model.py index e589940..f9adcff 100644 --- a/pyecsca/sca/attack/leakage_model.py +++ b/pyecsca/sca/attack/leakage_model.py @@ -5,6 +5,8 @@ from typing import Literal, ClassVar from numpy.random import default_rng from public import public +from ...sca.trace import Trace + if sys.version_info[0] < 3 or sys.version_info[0] == 3 and sys.version_info[1] < 10: def hw(i): return bin(i).count("1") @@ -14,7 +16,18 @@ else: @public -class NormalNoice: +class Noise: + pass + + +@public +class ZeroNoise(Noise): + def __call__(self, *args, **kwargs): + return args[0] + + +@public +class NormalNoice(Noise): """ https://www.youtube.com/watch?v=SAfq55aiqPc """ @@ -24,8 +37,11 @@ class NormalNoice: self.mean = mean self.sdev = sdev - def __call__(self, *args, **kwargs) -> float: - return args[0] + self.rng.normal(self.mean, self.sdev) + def __call__(self, *args, **kwargs): + arg = args[0] + if isinstance(arg, Trace): + return Trace(arg.samples + self.rng.normal(self.mean, self.sdev, len(arg.samples))) + return arg + self.rng.normal(self.mean, self.sdev) @public diff --git a/pyecsca/sca/re/rpa.py b/pyecsca/sca/re/rpa.py index 51b9738..212e147 100644 --- a/pyecsca/sca/re/rpa.py +++ b/pyecsca/sca/re/rpa.py @@ -5,8 +5,12 @@ Provides functionality inspired by the Refined-Power Analysis attack by Goubin. `<https://dl.acm.org/doi/10.5555/648120.747060>`_ """ from public import public -from typing import MutableMapping, Optional +from typing import MutableMapping, Optional, Callable, List +from collections import Counter +from sympy import FF, sympify, Poly, symbols + +from ...ec.coordinates import AffineCoordinateModel from ...ec.formula import ( FormulaAction, DoublingFormula, @@ -16,9 +20,12 @@ from ...ec.formula import ( DifferentialAdditionFormula, LadderFormula, ) -from ...ec.mult import ScalarMultiplicationAction, PrecomputationAction +from ...ec.mod import Mod +from ...ec.mult import ScalarMultiplicationAction, PrecomputationAction, ScalarMultiplier +from ...ec.params import DomainParameters +from ...ec.model import ShortWeierstrassModel, MontgomeryModel from ...ec.point import Point -from ...ec.context import Context, Action +from ...ec.context import Context, Action, local @public @@ -79,3 +86,100 @@ class MultipleContext(Context): def __repr__(self): return f"{self.__class__.__name__}({self.base!r}, multiples={self.points.values()!r})" + + +def rpa_point_0y(params: DomainParameters) -> Optional[Point]: + """Construct an (affine) RPA point (0, y) for given domain parameters.""" + if isinstance(params.curve.model, ShortWeierstrassModel): + if not params.curve.parameters["b"].is_residue(): + return None + y = params.curve.parameters["b"].sqrt() + # TODO: We can take the negative as well. + return Point(AffineCoordinateModel(params.curve.model), x=Mod(0, params.curve.prime), y=y) + elif isinstance(params.curve.model, MontgomeryModel): + return Point(AffineCoordinateModel(params.curve.model), x=Mod(0, params.curve.prime), + y=Mod(0, params.curve.prime)) + else: + raise NotImplementedError + + +def rpa_point_x0(params: DomainParameters) -> Optional[Point]: + """Construct an (affine) RPA point (x, 0) for given domain parameters.""" + if isinstance(params.curve.model, ShortWeierstrassModel): + if (params.order * params.cofactor) % 2 != 0: + return None + k = FF(params.curve.prime) + expr = sympify("x^3 + a * x + b", evaluate=False) + expr = expr.subs("a", k(int(params.curve.parameters["a"]))) + expr = expr.subs("b", k(int(params.curve.parameters["b"]))) + poly = Poly(expr, symbols("x"), domain=k) + roots = poly.ground_roots() + # TODO: There may be more roots. + if not roots: + return None + x = Mod(int(next(iter(roots.keys()))), params.curve.prime) + return Point(AffineCoordinateModel(params.curve.model), x=x, y=Mod(0, params.curve.prime)) + elif isinstance(params.curve.model, MontgomeryModel): + return Point(AffineCoordinateModel(params.curve.model), x=Mod(0, params.curve.prime), + y=Mod(0, params.curve.prime)) + else: + raise NotImplementedError + + +def rpa_distinguish(params: DomainParameters, mults: List[ScalarMultiplier], oracle: Callable[[int, Point], bool]) -> List[ScalarMultiplier]: + """ + Distinguish the scalar multiplier used (from the possible :paramref:`~.rpa_distinguish.mults`) using + an RPA :paramref:`~.rpa_distinguish.oracle`. + + :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. + :return: The list of possible multipliers after distinguishing (ideally just one). + """ + P0 = rpa_point_0y(params) or rpa_point_x0(params) + if not P0: + raise ValueError("There are no RPA-points on the provided curve.") + print(f"Got RPA point {P0}") + while True: + scalar = int(Mod.random(params.order)) + print(f"Got scalar {scalar}") + print([mult.__class__.__name__ for mult in mults]) + mults_to_multiples = {} + counts: Counter = Counter() + for mult in mults: + with local(MultipleContext()) as ctx: + mult.init(params, params.generator) + mult.multiply(scalar) + multiples = set(ctx.points.values()) + mults_to_multiples[mult] = multiples + counts.update(multiples) + + # TODO: This lower part can be repeated a few times for the same scalar above, which could reuse + # the computed multiples. Can be done until there is some distinguishing multiple. + # However, the counts variable needs to be recomputed for the new subset of multipliers. + nhalf = len(mults) / 2 + best_distinguishing_multiple = None + best_count = None + best_nhalf_distance = None + for multiple, count in counts.items(): + if best_distinguishing_multiple is None or abs(count - nhalf) < best_nhalf_distance: + best_distinguishing_multiple = multiple + best_count = count + best_nhalf_distance = abs(count - nhalf) + print(f"Chosen best distinguishing multiple {best_distinguishing_multiple} count={best_count} n={len(mults)}") + if best_count in (0, len(mults)): + continue + + multiple_inverse = Mod(best_distinguishing_multiple, params.order).inverse() + P0_inverse = params.curve.affine_multiply(P0, int(multiple_inverse)) + response = oracle(scalar, P0_inverse) + print(f"Oracle response -> {response}") + for mult in mults: + print(mult.__class__.__name__, best_distinguishing_multiple in mults_to_multiples[mult]) + filt = (lambda mult: best_distinguishing_multiple in mults_to_multiples[mult]) if response else (lambda mult: best_distinguishing_multiple not in mults_to_multiples[mult]) + mults = list(filter(filt, mults)) + print([mult.__class__.__name__ for mult in mults]) + print() + + if len(mults) == 1: + return mults diff --git a/pyecsca/sca/re/zvp.py b/pyecsca/sca/re/zvp.py new file mode 100644 index 0000000..22f6f79 --- /dev/null +++ b/pyecsca/sca/re/zvp.py @@ -0,0 +1,34 @@ +""" +Provides functionality inspired by the Zero-value point attack. + + Zero-Value Point Attacks on Elliptic Curve Cryptosystem, Toru Akishita & Tsuyoshi Takagi , ISC '03 + `<https://doi.org/10.1007/10958513_17>`_ +""" +from typing import List + +from sympy import symbols + +from ...ec.context import DefaultContext, local +from ...ec.formula import Formula +from ...ec.mod import SymbolicMod +from ...ec.point import Point +from ...misc.cfg import TemporaryConfig + + +def unroll_formula(formula: Formula, prime: int) -> List[SymbolicMod]: + """ + Unroll a given formula symbolically to obtain symbolic expressions for its intermediate values. + + :param formula: Formula to unroll. + :param prime: Field to unroll over. + :return: List of symbolic intermediate values. + """ + inputs = [Point(formula.coordinate_model, + **{var: SymbolicMod(symbols(var + str(i)), prime) for var in formula.coordinate_model.variables}) + for i in + range(1, 1 + formula.num_inputs)] + params = {var: SymbolicMod(symbols(var), prime) for var in formula.coordinate_model.curve_model.parameter_names} + with local(DefaultContext()) as ctx, TemporaryConfig() as cfg: + cfg.ec.mod_implementation = "symbolic" + formula(prime, *inputs, **params) + return [op_result.value for op_result in ctx.actions.get_by_index([0])[0].op_results] diff --git a/pyecsca/sca/stacked_traces/combine.py b/pyecsca/sca/stacked_traces/combine.py index 5849150..cbdfe04 100644 --- a/pyecsca/sca/stacked_traces/combine.py +++ b/pyecsca/sca/stacked_traces/combine.py @@ -8,8 +8,8 @@ from math import sqrt from public import public from typing import Callable, Union, Tuple, cast -from pyecsca.sca.trace.trace import CombinedTrace -from pyecsca.sca.stacked_traces import StackedTraces +from ...sca.trace.trace import CombinedTrace +from ...sca.stacked_traces import StackedTraces TPB = Union[int, Tuple[int, ...]] CudaCTX = Tuple[ diff --git a/pyecsca/sca/stacked_traces/stacked_traces.py b/pyecsca/sca/stacked_traces/stacked_traces.py index 09169bd..f2c67fc 100644 --- a/pyecsca/sca/stacked_traces/stacked_traces.py +++ b/pyecsca/sca/stacked_traces/stacked_traces.py @@ -4,7 +4,7 @@ import numpy as np from public import public from typing import Any, Mapping, Sequence -from pyecsca.sca.trace_set.base import TraceSet +from ...sca.trace_set.base import TraceSet @public diff --git a/pyecsca/sca/trace/trace.py b/pyecsca/sca/trace/trace.py index 367125b..4388643 100644 --- a/pyecsca/sca/trace/trace.py +++ b/pyecsca/sca/trace/trace.py @@ -78,7 +78,7 @@ class Trace: def __hash__(self): # This will have collisions, but those can be sorted out by the equality check above. - return hash(str(self.samples)) + hash(self.meta) + return hash((str(self.samples), tuple(self.meta.items()))) def with_samples(self, samples: ndarray) -> "Trace": """ diff --git a/pyecsca/sca/trace_set/inspector.py b/pyecsca/sca/trace_set/inspector.py index 2c9d581..9e18b04 100644 --- a/pyecsca/sca/trace_set/inspector.py +++ b/pyecsca/sca/trace_set/inspector.py @@ -146,7 +146,7 @@ class InspectorTraceSet(TraceSet): } @classmethod - def read(cls, input: Union[str, Path, bytes, BinaryIO]) -> "TraceSet": + def read(cls, input: Union[str, Path, bytes, BinaryIO]) -> "InspectorTraceSet": """ Read Inspector trace set from file path, bytes or file-like object. @@ -207,7 +207,7 @@ class InspectorTraceSet(TraceSet): return result, tags @classmethod - def inplace(cls, input: Union[str, Path, bytes, BinaryIO]) -> "TraceSet": + def inplace(cls, input: Union[str, Path, bytes, BinaryIO]) -> "InspectorTraceSet": raise NotImplementedError def write(self, output: Union[str, Path, BinaryIO]): diff --git a/pyproject.toml b/pyproject.toml index c8a704d..baa97f4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,7 +39,9 @@ "datashader", "xarray", "astunparse", - "numba==0.57.1" + "numba==0.57.1", + "networkx", + "importlib-resources" ] [project.urls] @@ -56,7 +58,7 @@ "leia" = ["smartleia"] "gmp" = ["gmpy2"] "dev" = ["mypy", "flake8", "interrogate", "pyinstrument", "black", "types-setuptools"] -"test" = ["nose2", "parameterized", "coverage"] +"test" = ["pytest>=7.0.0", "coverage", "pytest-cov", "pytest-sugar"] "doc" = ["sphinx", "sphinx-autodoc-typehints", "nbsphinx", "sphinx-paramlinks", "sphinx_design"] [tool.setuptools.packages.find] @@ -65,3 +67,12 @@ namespaces = true [tool.setuptools.package-data] pyecsca = ["ec/efd/*/*", "ec/efd/*/*/*", "ec/efd/*/*/*/*", "ec/std/*", "ec/std/*/*"] + +[tool.pytest.ini_options] +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/conftest.py b/test/conftest.py new file mode 100644 index 0000000..5c3f855 --- /dev/null +++ b/test/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/data/__init__.py b/test/data/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/data/__init__.py diff --git a/test/data/divpoly/__init__.py b/test/data/divpoly/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/data/divpoly/__init__.py diff --git a/test/data/divpoly/mult_21.json b/test/data/divpoly/mult_21.json new file mode 100644 index 0000000..48cbffa --- /dev/null +++ b/test/data/divpoly/mult_21.json @@ -0,0 +1,2655 @@ +{ + "mx": [ + { + "(441, )": 1, + "(439, )": 97020, + "(438, )": 201598948346408346749138595589353587508, + "(437, )": 2275087122, + "(436, )": 310927262294181614419684484799262713780, + "(435, )": 317115504108870610776237589705194616859, + "(434, )": 238190928241589059369233464629769964853, + "(433, )": 266943660106099362756522197679976453573, + "(432, )": 153402333328171964086545270263710950709, + "(431, )": 36156248285858384861491399844719874576, + "(430, )": 68618309294440875124825847895032400444, + "(429, )": 81940275054596141681907203204492700810, + "(428, )": 273753275173965199194690519988001633001, + "(427, )": 127847879045220942151327669972684376682, + "(426, )": 240758424215832426364068522313012307488, + "(425, )": 217871952225849850895313921444897728002, + "(424, )": 232672952704936920906008614649513644537, + "(423, )": 185901179922516094335268011309493505746, + "(422, )": 71804483167657044917361360877762428045, + "(421, )": 328924279902831759147615962118628857414, + "(420, )": 213847077814364959620618689284156308308, + "(419, )": 301963109234488219225325411695528431176, + "(418, )": 142445144641188869013561649444761773594, + "(417, )": 327586862048307731115415194150731394450, + "(416, )": 272376532309912010065408557454171503251, + "(415, )": 185973012054262471449814863116686785056, + "(414, )": 262964103403213342622112261573457138827, + "(413, )": 146290005613090870012473748380231850753, + "(412, )": 56303885539055160848300484988550228974, + "(411, )": 333562722813401640101596406354852585620, + "(410, )": 235148570567944996746039107624091955747, + "(409, )": 41393170909955506026232732971622861184, + "(408, )": 63413117717684753646768717524553451868, + "(407, )": 107976081242114324264908973696825563872, + "(406, )": 14202008571489982110582297357361445421, + "(405, )": 145587211802351290421216675037319750324, + "(404, )": 119591548141022064657984141234857404996, + "(403, )": 191296977171299053949529654508561863636, + "(402, )": 174417341429655887445754612203640100094, + "(401, )": 130147058822586892633439673761690971876, + "(400, )": 168649173072565927025228844545805928937, + "(399, )": 24284290393331064466349364921333227450, + "(398, )": 326689030046966179294090309774332684729, + "(397, )": 253454171712156501293158273942654984982, + "(396, )": 2377564135650784239731885354448370314, + "(395, )": 266946719784124013108802285620338595588, + "(394, )": 198038095484422492233132035142816231324, + "(393, )": 322754405494530684447473983660907223523, + "(392, )": 134735236013828689669783344861934666613, + "(391, )": 28993671125729325173659240067668932758, + "(390, )": 210414684507736204127172002492286362435, + "(389, )": 128292972085545059915021611868095008525, + "(388, )": 154294474791779794351921114336902507332, + "(387, )": 293638412369158419952600069079428070631, + "(386, )": 15937170635697155843421759986482269349, + "(385, )": 254556806205834611498245593556508856349, + "(384, )": 106682839681744230138142869149594605744, + "(383, )": 337943938460632327044816057116099049622, + "(382, )": 331609468588999374830320006211796187798, + "(381, )": 117579206076478060658278584032868896729, + "(380, )": 11443394716770489019879137007692664899, + "(379, )": 303135506971633991042615404238108774106, + "(378, )": 208510295416554701524381039198388799805, + "(377, )": 109487310941930016575272627881419383934, + "(376, )": 119156583731204455826024510875480593997, + "(375, )": 137774341493838036431893685183253746601, + "(374, )": 165433541951145498207226457398777769198, + "(373, )": 271088806339047508848043870357027007614, + "(372, )": 17762796589534617769710665713177840068, + "(371, )": 96390389702357371564869148251081920976, + "(370, )": 158752633248117234893337683528667126941, + "(369, )": 232610581771572299282056823559505701856, + "(368, )": 132443924448906020019465287028487368325, + "(367, )": 130718528882110797421724471162040858869, + "(366, )": 250667224314238998743937062726538093014, + "(365, )": 86091785757609782501334114312911182592, + "(364, )": 333855312884459020774564449386569472981, + "(363, )": 219336857361305061072572349936345758137, + "(362, )": 339321972032800035442104120130694853714, + "(361, )": 117688460350761414110080839606608812091, + "(360, )": 10305998390280874116638184880217185934, + "(359, )": 112428680577179613052971663542897078207, + "(358, )": 142807399134714191572173344334236973269, + "(357, )": 163688402059398261484897656845157841365, + "(356, )": 285478028132761619953175014238074755983, + "(355, )": 184268815268580435040773191309599858816, + "(354, )": 52940337960788099324118751017968384589, + "(353, )": 234925443340083138487935803852969609268, + "(352, )": 103202033462272873563809651975746877332, + "(351, )": 122417750132724102881819625216481220278, + "(350, )": 36291872418828397993920867899551653754, + "(349, )": 225911899488326820146377927228375380475, + "(348, )": 231209822301227773724655991503365677112, + "(347, )": 154613288723197989710294465073059415351, + "(346, )": 317154941611151795644647289466125108516, + "(345, )": 35134344409855494521532234124586436029, + "(344, )": 18318239337787000274845161034286523344, + "(343, )": 263030184889358333679156807800992365763, + "(342, )": 239233917129296608365988828518443883955, + "(341, )": 245759100370003206102043147724251463812, + "(340, )": 131664244334700997594755034850428092520, + "(339, )": 73646504382319380782633661107221898542, + "(338, )": 117871760754826501971048585481472894081, + "(337, )": 57780194985122501082000345293170897504, + "(336, )": 55618947435831685549398775134529344013, + "(335, )": 256526002354306834146777083253494695606, + "(334, )": 156396395661307658011468137161398923274, + "(333, )": 40499396482412728600678402400219405867, + "(332, )": 8618851443185748762470646085401555453, + "(331, )": 101119930000666808830737425694658611542, + "(330, )": 147441382500553470814308140925047585872, + "(329, )": 237349408386789139465038840577749324622, + "(328, )": 129931439177416879029264629085412967406, + "(327, )": 46031949832238926021858740668844782615, + "(326, )": 204376523475693267104334187208753245236, + "(325, )": 245485944385965646389021304491312692908, + "(324, )": 161341455731438424590650401623491874705, + "(323, )": 318837270671128640744903513975087529250, + "(322, )": 173361319083376533046790005046710234887, + "(321, )": 210008900362286193035247194444010106071, + "(320, )": 239745008473263324476607387370455605651, + "(319, )": 331725036142064444330064940141863545304, + "(318, )": 75756305036865478074469353065287909215, + "(317, )": 94617616681351920531854375980784302447, + "(316, )": 11044898908781085099625367264609292830, + "(315, )": 243272495764694639684762789870792992850, + "(314, )": 195814871244278531434328383496929092646, + "(313, )": 131262954174303734074569934081885400292, + "(312, )": 289711941919466128129391457912447087410, + "(311, )": 176833573271385887623529168237585575199, + "(310, )": 320614168818864495038930655185925900725, + "(309, )": 192882298293840845982761400870831395288, + "(308, )": 45224405385678327370890595432527068551, + "(307, )": 111071247173430170888252804473123351454, + "(306, )": 103792075787720512104131565014551530392, + "(305, )": 185169794995380653785533798940598436063, + "(304, )": 308599408841508364784356902710706362263, + "(303, )": 301084486143133189703018414829528585893, + "(302, )": 147246278025768969109897289766423081356, + "(301, )": 104659145200821087890231741360122925507, + "(300, )": 241887527954030462618204153922473247269, + "(299, )": 225118567436612830577702638028033035614, + "(298, )": 329850728543791265799120390115341846778, + "(297, )": 124329001804718987989270750855983154836, + "(296, )": 77494943934880393564492797477955571306, + "(295, )": 35844983648460980397312261101400671412, + "(294, )": 285539275323895630114837103615522235537, + "(293, )": 65319844603627599719466112221441916912, + "(292, )": 326668212931717786494280779402870589426, + "(291, )": 19196893031895824489520264781189108919, + "(290, )": 312567580761706482497863280165700716443, + "(289, )": 219211407955099550119115081515206857557, + "(288, )": 187382666919641367012948635867316697545, + "(287, )": 188792639874882687879814970060940441638, + "(286, )": 104957756923069370494274664808512394668, + "(285, )": 6229970600772557627622005661235795134, + "(284, )": 120984990640582898669120069422112314204, + "(283, )": 83008022858463538496098504623520990054, + "(282, )": 186486862218848904516874316756959990019, + "(281, )": 161761762385015609470541514966054031892, + "(280, )": 93687743656189326463782473151958161042, + "(279, )": 229837813728569029516886921977700292341, + "(278, )": 243121785684739209623364533526656383563, + "(277, )": 66335528926735779057043836343188205468, + "(276, )": 95148252763381049972162179412452887888, + "(275, )": 100569597192190476458102758148771883029, + "(274, )": 249103191625782863113515022864853602809, + "(273, )": 261924318498154198383397389529424662770, + "(272, )": 63740731308446457073493590404384208186, + "(271, )": 242786502504464173990994718604001547371, + "(270, )": 178278624240702538270490407401404653958, + "(269, )": 279865702387241398115874657224365380676, + "(268, )": 174537669878927887162859319092305982672, + "(267, )": 87232855049292505455894293008042261704, + "(266, )": 191017856832484695560511861959295196156, + "(265, )": 139847865583383581760015528696201932061, + "(264, )": 128688000514920428908649192211411082688, + "(263, )": 247281474963512755638914096851400554890, + "(262, )": 221435214144339981041078172650409422290, + "(261, )": 244241660848945214666251475509059431210, + "(260, )": 308408741860281681991194536282307897754, + "(259, )": 136393741124551059805626255769344527247, + "(258, )": 46805288958965070745765921644811012731, + "(257, )": 226209005273482670725479525178980654281, + "(256, )": 302845603689940847947396737732062052366, + "(255, )": 225697754817650995926889942027583962553, + "(254, )": 336357005383311103344651633881649733102, + "(253, )": 278453258633105061254331842586691493794, + "(252, )": 197917881895763148757602470505147772564, + "(251, )": 32963044278178100002928971415239102619, + "(250, )": 169405515805487465541858077176078892593, + "(249, )": 54239763060729779687622681371960887919, + "(248, )": 13580036187058208789514932133314080949, + "(247, )": 204874968039980489621036226018268892222, + "(246, )": 44748315624751184341607925000092044722, + "(245, )": 202099559385412234257348154207717789255, + "(244, )": 300427428706225712262133286003997098296, + "(243, )": 171195113001299024495192603719779610176, + "(242, )": 44751574993773033753351494040154613039, + "(241, )": 236740128449651842267469028893053899807, + "(240, )": 243827045965232143328755833335267105220, + "(239, )": 165575333559622408970448065516488248784, + "(238, )": 315707792911137181918942599619199567584, + "(237, )": 111784066211592730387397929063424341989, + "(236, )": 46025156782724558035637993768706387790, + "(235, )": 278180811767935363293622462360102154858, + "(234, )": 216983454093124266791205574366784771542, + "(233, )": 70634060337761702788544976853841702984, + "(232, )": 237465165779370751553121026679974258857, + "(231, )": 277003368019691372353352545972135455190, + "(230, )": 158215812991267889714235198089381240863, + "(229, )": 37058883453036728383368447233498907395, + "(228, )": 159415030378147572005595509098851486683, + "(227, )": 104715581993305072535674812704703191622, + "(226, )": 130194725305907925895509998130707395655, + "(225, )": 54170119604911522747043688691134898977, + "(224, )": 271710272810585935398666491072396821427, + "(223, )": 154832519654847823248190540113164900416, + "(222, )": 31683726362235972683174906632109423596, + "(221, )": 281828970789222666498055138417545985614, + "(220, )": 231595016585561851889956460296025080, + "(219, )": 324222189460329266485490747727443463561, + "(218, )": 91247293668902012672950649839499052753, + "(217, )": 31407509602119338590075279226429413488, + "(216, )": 170611989430027210602805170003417329137, + "(215, )": 107604900277664217869353369547731865860, + "(214, )": 326451504732837904467096217284817147077, + "(213, )": 170917583423849892922724428116760250251, + "(212, )": 160626311783467723825429999844649576812, + "(211, )": 183848220517598196190234822178021439891, + "(210, )": 309782572134890608815530179046787660890, + "(209, )": 220263089198665719161846823840315394233, + "(208, )": 8644806525413275781945145107206123182, + "(207, )": 20851910290006859057512892503690382688, + "(206, )": 218684336470313469266194008793666914136, + "(205, )": 149861304045318257927240767216653109923, + "(204, )": 56084453613460202979828566519060213234, + "(203, )": 124328105791369544330306492914766381989, + "(202, )": 184210113942090198015083409618448379980, + "(201, )": 22584189839794165620840178310900471574, + "(200, )": 324447507136149006018478178445553809239, + "(199, )": 57412380321424742937141375961962411738, + "(198, )": 58157607670498218595035852083829220560, + "(197, )": 43904907699849846215617735053641735705, + "(196, )": 52238169114326661567509924248011149410, + "(195, )": 178962017195674465389997270669587738441, + "(194, )": 320085271054247591671020176920682220953, + "(193, )": 182572755577066666778948292840520690042, + "(192, )": 98340301400655714154217580186521202073, + "(191, )": 133321425498751528586508407694382757009, + "(190, )": 62914336972736509070965737361371652919, + "(189, )": 15251451368642550114421013127010303367, + "(188, )": 207552468437509785817274380848657641677, + "(187, )": 38264363837323908509857489769894231366, + "(186, )": 209680564959616147168177856599876393663, + "(185, )": 4187331146218846817744559160572463157, + "(184, )": 91598091373307760032202631639451784568, + "(183, )": 71242792181361944088984414617376427901, + "(182, )": 291394576189965198124342542979308671250, + "(181, )": 325915934401867240099937935691174572810, + "(180, )": 79915098167806909060933744665002793053, + "(179, )": 133249317771536310979833721115464552843, + "(178, )": 188599866277146255706193730810586587453, + "(177, )": 134721706216780043764173406746876154192, + "(176, )": 253581166559489775313533464468798072751, + "(175, )": 332416082530597894132715331643798797534, + "(174, )": 100751931061353586424970537764057405492, + "(173, )": 69441494541392728304319123269674460311, + "(172, )": 271400883502314307079168546996437365202, + "(171, )": 272016239058438978264979961031067458281, + "(170, )": 240308319781137078202533474877609945469, + "(169, )": 330320008613089596908014436059842158620, + "(168, )": 3241449527094841056662891294281882744, + "(167, )": 60466715981548127800230137891093041246, + "(166, )": 227169035601680143073433411370215069287, + "(165, )": 293225105724477005256419568907987565660, + "(164, )": 115389618877503150921977826305862742009, + "(163, )": 311906595383266871426971958379505069046, + "(162, )": 75958486306231516233680921380398052605, + "(161, )": 261565437930143737093915650403627026281, + "(160, )": 220367267951525163129942168245151517620, + "(159, )": 6853180945661662986442678721110188588, + "(158, )": 303270289825012805239198355475327269145, + "(157, )": 271690786488530729356250573014707458957, + "(156, )": 79384069775347411502534173349810701716, + "(155, )": 125738695753625404475409200711868887171, + "(154, )": 265731054705676209532536849960434164662, + "(153, )": 182820635343102126152946344453947245508, + "(152, )": 74534267367059142658245734891314529588, + "(151, )": 114912469964451862360526526759339672374, + "(150, )": 151516749560799125436838515198355262860, + "(149, )": 301361015759271037783324519455560025348, + "(148, )": 235694758047571370940312077469819556081, + "(147, )": 226861968722301633755882605680516328753, + "(146, )": 41049804028156940592595912282297221131, + "(145, )": 305299637533178699370660159974543702304, + "(144, )": 130145661368107556171825423636491067294, + "(143, )": 230015703871707359639818574204272225754, + "(142, )": 330314391516603166716764360206977938488, + "(141, )": 23900714190716775402005751607003786957, + "(140, )": 336147636104052615279694709479075489503, + "(139, )": 245109870408212582318057482988638313633, + "(138, )": 300670052615479835072538431558676146909, + "(137, )": 19228793548802218875404534870961194813, + "(136, )": 63023175247052200544506229746302842549, + "(135, )": 133275478373540124529441268300065824448, + "(134, )": 305074381152628659733278422377514738460, + "(133, )": 100262470415968315196956576405141812020, + "(132, )": 192021641481122999950115170210430821305, + "(131, )": 18305314625597843152301379073032460852, + "(130, )": 243000406168917118996021303903399118802, + "(129, )": 134701535946144018518280686142681894738, + "(128, )": 122396627172813488737603531878221966864, + "(127, )": 11739746199354246218349038154305551295, + "(126, )": 102150936510735672662504313050456552115, + "(125, )": 308475869111634760665726879262287224783, + "(124, )": 58134697884515267612102547730513199554, + "(123, )": 232459068252094978730041099443975747119, + "(122, )": 252484985015697093096599542850753444303, + "(121, )": 49596962725529252908372217419205748135, + "(120, )": 30901114625862507431026994987259563195, + "(119, )": 236039137683024734342372783770425456438, + "(118, )": 234831851350407853468840648852408827306, + "(117, )": 174635163864205022112742787366310978420, + "(116, )": 154698751164617972108452545867725755058, + "(115, )": 123570329296187999463185705610913914580, + "(114, )": 55813035634587949683256788070630575560, + "(113, )": 284390112999300439109615753472556498057, + "(112, )": 91431639600807753964968027258040190788, + "(111, )": 286791145872526646350271481414524618531, + "(110, )": 317968175946948298118869564181190202055, + "(109, )": 315363589365915304060936877502578182230, + "(108, )": 325066570254460407460578006121017006021, + "(107, )": 66126495439077092589305089224956466094, + "(106, )": 329785625469704754061121719895926064193, + "(105, )": 287209560053824675765265991467341672836, + "(104, )": 23883590460834210903620433761270176699, + "(103, )": 230691474556566263314398473395696987030, + "(102, )": 281121911936210030332093104492202236992, + "(101, )": 229493211210843637293851400465946780778, + "(100, )": 8346048265596523956895990721378566211, + "(99, )": 312075734021763297478529394319707414887, + "(98, )": 70290669087917785710193171288526747855, + "(97, )": 36869690097405253736171970707808378673, + "(96, )": 197937440433595781378574674005968937776, + "(95, )": 87871135273153054701213321873215834368, + "(94, )": 66783672985478748617592338679887064903, + "(93, )": 151644456000674461306552632059295414898, + "(92, )": 229019938655506334294384503287381219823, + "(91, )": 99500390603452290107810077626861412168, + "(90, )": 293995183402692093260777158295021934888, + "(89, )": 231630855415824631088282056088029975615, + "(88, )": 315190528120149793068907513889130985259, + "(87, )": 39391448992147121885410105601462357673, + "(86, )": 293543452321522390777942015401898792359, + "(85, )": 24649375785611773263400926339478405265, + "(84, )": 240872318316709102176547063970097656380, + "(83, )": 183526711810047906880212068769733982387, + "(82, )": 257842308783317664301796828412346798588, + "(81, )": 49936107297292562551428406631513305627, + "(80, )": 45803140569212523317150252144644536287, + "(79, )": 7192925833597238534107355535199528934, + "(78, )": 256539443295052386221023848110791745723, + "(77, )": 155855054542096679150442633582062388577, + "(76, )": 10041787447488811741365481228736034281, + "(75, )": 118160228434263396147226334693169604240, + "(74, )": 38454605073052271158865970607098256918, + "(73, )": 252747625306539655963403167351100215110, + "(72, )": 76939491704717040651005055987507199913, + "(71, )": 225784265425754518495709634519051300999, + "(70, )": 197734196780618870057576060931213255234, + "(69, )": 22747119250934703255984259947161324870, + "(68, )": 237173259761459602737429439508516467112, + "(67, )": 149369914969347277594026488746590773357, + "(66, )": 145195311692643050980810218131847354089, + "(65, )": 93590407234288848990639441682021852956, + "(64, )": 247841952276313769825332521912751503047, + "(63, )": 54118901191443517070250548601985486436, + "(62, )": 47311103561763719008669247508971692965, + "(61, )": 215266983154379865521844494370287411314, + "(60, )": 199106775683283638727996368737815019188, + "(59, )": 63490807678296155582533323541121320095, + "(58, )": 40898500300775358933769952246472783782, + "(57, )": 7976201031532205306764376276468645521, + "(56, )": 128530894014297823266271104137203298687, + "(55, )": 153106156430977315312735022072603264095, + "(54, )": 291895594882496017353313380337427848815, + "(53, )": 125697265352495285228623498159810750877, + "(52, )": 156243545491951426973703036633345820039, + "(51, )": 166494512657545519707143097858645517794, + "(50, )": 17545649806805731680077542435328506984, + "(49, )": 84735514026036113309500934112610523362, + "(48, )": 54102418773983135702139135790814874073, + "(47, )": 223692601971635651076392348159416778571, + "(46, )": 272017522321897354081420307459003408082, + "(45, )": 119792452862595991836058891383644038795, + "(44, )": 142778975650728600005787188201629980433, + "(43, )": 290150048237463918590108203072939758405, + "(42, )": 148935513386187933767745198950767142523, + "(41, )": 9917809338260230480142131022184957022, + "(40, )": 327281863414046186981573673451635787306, + "(39, )": 54663296408720004394329886166435829596, + "(38, )": 239981330431249426842251214174379349613, + "(37, )": 337758229505096600956098524083493616709, + "(36, )": 252634475326521176838015794204553418925, + "(35, )": 31180251655522266557613296112312187757, + "(34, )": 44079208540206236827908778910962352366, + "(33, )": 250682759863906069336928902386107939839, + "(32, )": 66797797301445974619075052834166831077, + "(31, )": 169145615393683883086475628860701165529, + "(30, )": 338905684537211254788320184589077887822, + "(29, )": 52498502841622213057752195005332242647, + "(28, )": 178851689722118100523214706273592043422, + "(27, )": 71825342766230310105294482794310692907, + "(26, )": 289677341163612207715399265601872561069, + "(25, )": 236925198040579678942027946678869537085, + "(24, )": 134386226684684609227819392532162924850, + "(23, )": 221317458244257588945118479162062685686, + "(22, )": 60171929941764286196853012566006379292, + "(21, )": 279780359405134602453895310152504434924, + "(20, )": 135358445324866239900741527677260184456, + "(19, )": 294071560303592335124053797293279019957, + "(18, )": 74480744387788368558713000359402319189, + "(17, )": 76905125543079288351688363666309805436, + "(16, )": 221371062063899680105868913092053746514, + "(15, )": 63636281792073537233418192461060847798, + "(14, )": 25616638684508438553576354564618806069, + "(13, )": 217248264406182827770023658413224008213, + "(12, )": 325988819566852583032623155289226176825, + "(11, )": 32072119334060785804543679374811818803, + "(10, )": 275254141688470716649115782553925027803, + "(9, )": 182001442802124581848922700380200301420, + "(8, )": 13711710171648406044770139180866270306, + "(7, )": 324184189548634070784091701105955757710, + "(6, )": 288280673788849802995512341414972719795, + "(5, )": 17000327112279048912052162119381327572, + "(4, )": 254619592230160421754477147800808719354, + "(3, )": 156051637888178851935664257174652066283, + "(2, )": 179439419374757651990488155747802832596, + "(1, )": 159543934916531652563571025206865012651, + "(0, )": 74326875737871892797207286624209954525 + }, + { + "(440, )": 441, + "(438, )": 340282366762482138434845932244671637195, + "(437, )": 94272837787031781331747290627616492722, + "(436, )": 13800464370, + "(435, )": 80183708130810209828183058111183103334, + "(434, )": 95979575679762141691763754227747012385, + "(433, )": 2197279286266752937146945672797658906, + "(432, )": 238864491711088835977212132385584529510, + "(431, )": 185896658498438514841098887978414249667, + "(430, )": 331204208423123859927332123384656963116, + "(429, )": 192370308558257330350805288120538544601, + "(428, )": 188443932400177892899721689372254242823, + "(427, )": 327000200753412403693709196529238381398, + "(426, )": 52669826392758677331713790720879848419, + "(425, )": 60223222768282137267521160890685871813, + "(424, )": 279047939290236800397671594088004741051, + "(423, )": 22415020479219493287084117258096423294, + "(422, )": 330909592891290307157457066689154514282, + "(421, )": 71495011805856441408059042593976758788, + "(420, )": 84989231052644057890551155454525852727, + "(419, )": 13885865452288773361634391676682642072, + "(418, )": 239896591135433963774251897918870925568, + "(417, )": 97891743064069256852251910450888911444, + "(416, )": 248364318592807402398477459046814257114, + "(415, )": 295797569104589359093831826301865552383, + "(414, )": 229023336235800126264174541664940494381, + "(413, )": 279017966173064319071116933994619335571, + "(412, )": 285201709617599199017960463062428566275, + "(411, )": 182213436091222239770640619867063802377, + "(410, )": 93728292658922224041473115608203480663, + "(409, )": 143931851940060773994973722706347372323, + "(408, )": 160658650402142065252249079615650646817, + "(407, )": 328768420252122945269644243164842519762, + "(406, )": 261526008923442987682234429966671564656, + "(405, )": 13871811548450637435144991697765957862, + "(404, )": 299270881850333289106150866086162981711, + "(403, )": 137931124864937287765071758458770463989, + "(402, )": 197152747558309808689194521372782762072, + "(401, )": 304925729469799544149570020243706325407, + "(400, )": 137042430964782293626954208434206973741, + "(399, )": 31623065089666680612466757407058601332, + "(398, )": 10863110884990861474467734377825742175, + "(397, )": 234596043637945836127810503802331767302, + "(396, )": 224433532288653678408001485562108894289, + "(395, )": 110158615469299247677808345589999829509, + "(394, )": 43246352286967117549421241439910919959, + "(393, )": 146146004660080502362121913150825386431, + "(392, )": 296027356893580543662038661173991602538, + "(391, )": 139520646425289805873111897559608956570, + "(390, )": 138836591301560746823299082964226073554, + "(389, )": 220706461737147399951534432277440710958, + "(388, )": 243644293873637008138318810886241018149, + "(387, )": 21327404773200099251481426375896877517, + "(386, )": 228765725278783452810619032086959992495, + "(385, )": 45400733021642170856606162569729478850, + "(384, )": 28111254208161794610821662595565908571, + "(383, )": 199425422065514229658783719593560423377, + "(382, )": 328855789097884856961950643799297146141, + "(381, )": 303009802944481282243667117987393539136, + "(380, )": 83219995127753710764954466780452280263, + "(379, )": 90419400541837135719645623871244841991, + "(378, )": 12734501215599496551337332312947600350, + "(377, )": 304943044039727756579093519202524743804, + "(376, )": 181004532800595836372120969155542605811, + "(375, )": 151170616937984737753563203069379334407, + "(374, )": 27450509466894929208763189733712418052, + "(373, )": 232064547010873332996330979060689947817, + "(372, )": 76187668756519126780340548088084166932, + "(371, )": 301451228240293143283969288589576609705, + "(370, )": 17235844556528369133472880813678545208, + "(369, )": 230627988485740964374420228272772986880, + "(368, )": 147354599574027222632955913276146749626, + "(367, )": 331859182382687509317114675428976910341, + "(366, )": 144918777717342776200416161840797191069, + "(365, )": 126582524340649727184123430605397661793, + "(364, )": 194371840788833317159057216973730845803, + "(363, )": 20747218524365988817030033245777687082, + "(362, )": 148120041161755287492598283597626956729, + "(361, )": 331484832153113028294405158066211329588, + "(360, )": 44027391064181966547475345759389636088, + "(359, )": 18699638571386156428354271454589705724, + "(358, )": 304869373632422273392867790390982443239, + "(357, )": 290185054150202654164132579786976634885, + "(356, )": 128067454816841772137253729659167152793, + "(355, )": 293284951370547541640104166929673139503, + "(354, )": 173389543965837169219023935245833471399, + "(353, )": 303149456459484285422340387129560524068, + "(352, )": 252898566941611046721050462296279823433, + "(351, )": 180409740281931898655537952428966565721, + "(350, )": 238565879915572225843265960469609595888, + "(349, )": 17108527258838965769641426010581714202, + "(348, )": 315148040296915464377458776103621284413, + "(347, )": 68490669566724382319221127917200354372, + "(346, )": 225038142208166931493925110983152204103, + "(345, )": 33690116818266660341391807621811129392, + "(344, )": 10003317852741855643216675401591183694, + "(343, )": 93671764170962319141291541935865977985, + "(342, )": 116692977086549124347424616619392080102, + "(341, )": 181172021808293266655024738110807889875, + "(340, )": 212759493476786152463631101452540172594, + "(339, )": 107325241639333770801611880052202504630, + "(338, )": 135758853186147096299628998250896908497, + "(337, )": 262091841156642434138200485798765124890, + "(336, )": 230416276976566766127167631815227341098, + "(335, )": 24749116960613392998513198678683299951, + "(334, )": 338292280636594141245582046200236211648, + "(333, )": 231414956405351030822934482971251836702, + "(332, )": 53431738836370469463170755581300038316, + "(331, )": 284148176705893078346117906272174429304, + "(330, )": 205415968423770035077800968069568189651, + "(329, )": 307158229733244442942491047536128264815, + "(328, )": 318824090415286717844310634281747196785, + "(327, )": 111802376964334753458932856342145663075, + "(326, )": 173779832709071680859199743019764272269, + "(325, )": 186891156727628186081940065683745948130, + "(324, )": 251931642299097552432038874081065824457, + "(323, )": 142865820392824797790490219146666866177, + "(322, )": 163785835759660002457349217174980615656, + "(321, )": 195545322513751876172958809377107605404, + "(320, )": 40670982237589493916211279080876808254, + "(319, )": 93039193949362884594189174115105458595, + "(318, )": 337195670321378563952634248918250846188, + "(317, )": 129042041998944529626688774087331454518, + "(316, )": 60994022708869068531784724120421165183, + "(315, )": 257033564848824175272223925462873989976, + "(314, )": 330239112532282415437316223193494278022, + "(313, )": 35584315633058775768817602351109263869, + "(312, )": 311619183749976783519819547629238564954, + "(311, )": 241831574459927375261321325504334002906, + "(310, )": 63446102399920058097005737577364419825, + "(309, )": 5576167144405259205017479417245368972, + "(308, )": 149231124472184104675340321551108913373, + "(307, )": 199724326683710004384958278799203645033, + "(306, )": 72602564659014749889862634191296367366, + "(305, )": 287173835930379170338055381738897421813, + "(304, )": 41426263615035975808486016703434164715, + "(303, )": 85352586933158148295966359121125025410, + "(302, )": 291215847684405529391555892052079147931, + "(301, )": 286606629133094937317678850656395010367, + "(300, )": 305629227241006987264628432922496153378, + "(299, )": 198299257444381655409124301775194544606, + "(298, )": 73504860181207758809556429336418328580, + "(297, )": 271116686648286410656785087992696812332, + "(296, )": 331594532374244949831354622392391594609, + "(295, )": 193553438574791215706004868866938234197, + "(294, )": 339173912432962623614772069539930201607, + "(293, )": 281732680609534700880156705334368068676, + "(292, )": 282172274409038078833623556065237229442, + "(291, )": 321236953017197727050957135432126854061, + "(290, )": 276921899786883197139718298551655927610, + "(289, )": 43223554258981284990953806949002657823, + "(288, )": 61010755788119680204803025653795212939, + "(287, )": 161500287406837046168070960490367399997, + "(286, )": 137531064011899887593019183422977368128, + "(285, )": 225762971652215331536039873911153978675, + "(284, )": 91529027268066032522006674412453020176, + "(283, )": 262144073032506017632134589206291945600, + "(282, )": 144863332189945386942788857078523903370, + "(281, )": 5645122840705188172781146284471867226, + "(280, )": 189780058696547012573922033814390090188, + "(279, )": 86959071530190470003309872978417599401, + "(278, )": 256678558175444502729788771434476826918, + "(277, )": 128274242389455987868567714351985194860, + "(276, )": 224638107774902804590679059453820009218, + "(275, )": 51250584244998318255570657306518708212, + "(274, )": 88562310885339435869117416255438323791, + "(273, )": 266320616358849843178360620339939460133, + "(272, )": 233569740854655600196574576427735277503, + "(271, )": 182512720209000375749127637656664117390, + "(270, )": 198676233587504701944947476935596851770, + "(269, )": 205430107917634213955001454051324435031, + "(268, )": 336726218514473695397305278150921747573, + "(267, )": 118508325491754017804252961673833601566, + "(266, )": 5817338994650765766767875672982229470, + "(265, )": 312191545593482092428944292210791246667, + "(264, )": 214338771189764691517939541027817406184, + "(263, )": 102406807217066212900098469238355237223, + "(262, )": 299563178973973493660139959091994451271, + "(261, )": 117728163955750874289830057802423933023, + "(260, )": 34013777467844118410759704333138493105, + "(259, )": 153337844228220380388014425734620207831, + "(258, )": 128538628085094653062125280572332728217, + "(257, )": 235409187024488044896170572297173409404, + "(256, )": 195900898803439846235163095893948922633, + "(255, )": 334803832370871131575270994315754697096, + "(254, )": 138811592121170099861668236477558515922, + "(253, )": 246301063300127828991385628667287464084, + "(252, )": 40621610279402530821540036665447047712, + "(251, )": 149985837521626550364486629068023502042, + "(250, )": 324305676501134992481701464610887793266, + "(249, )": 194974483390068162088280100738382226506, + "(248, )": 305511594242393980362927228683456177342, + "(247, )": 145182317838485316307977923862010820634, + "(246, )": 246550530967192389218127127143881614822, + "(245, )": 111039152332858471134213548425861154262, + "(244, )": 186952412101327885947140724628709810939, + "(243, )": 251481374489452682845488731920457391019, + "(242, )": 140269157719509800788899520930432404756, + "(241, )": 207244301392004691743251713742756568565, + "(240, )": 334999046664397618960989358920314455022, + "(239, )": 260149620539840459928929234904603514896, + "(238, )": 122700932704777848946921393139411296150, + "(237, )": 263100329029791925351126370089547734644, + "(236, )": 140437198391322221349892775468983672737, + "(235, )": 337473425794598221158408707944026603723, + "(234, )": 34666751817150387854249175956729252361, + "(233, )": 177203178531926941643937140197357204362, + "(232, )": 159010593036938644936690496549513112620, + "(231, )": 94638689545399892629580475622686259268, + "(230, )": 11993516348001315696657183777784864341, + "(229, )": 303841714628127487305203173651428071764, + "(228, )": 17439690508897507311494362974308312182, + "(227, )": 53138397918495276049646244003625300010, + "(226, )": 128192571041098635795049369570055636571, + "(225, )": 217991474631158201296623495582108950945, + "(224, )": 147854234330877566548529674620066728144, + "(223, )": 246367154988429153565052679509242154851, + "(222, )": 89470268405558711021722594105318799506, + "(221, )": 288667415132379703365215887051014206321, + "(220, )": 50318185115512431144235264871230987581, + "(219, )": 91099120565801037014593745018804675105, + "(218, )": 338862221439289651043585956755005230028, + "(217, )": 125022533821435633371834103463515392296, + "(216, )": 273799241806648539902269793571654323213, + "(215, )": 155179193110507414082967892859985068351, + "(214, )": 264218535952060016254861222999768327765, + "(213, )": 274909560349355972131904519040473929541, + "(212, )": 223490200311452006732962725712417565436, + "(211, )": 302336912876099219840606128270150186423, + "(210, )": 15701530930176776307425897885097561345, + "(209, )": 11869181897095011518699510336513430922, + "(208, )": 105135348771424202648123754077655480798, + "(207, )": 29610866513306518065727651315167746816, + "(206, )": 50225384560320033160308106611426730639, + "(205, )": 245637812400386505098392357684496982922, + "(204, )": 306107039260249390518872556520913828618, + "(203, )": 79714899008849194794108906378260297338, + "(202, )": 48127767028354670277231657101760129701, + "(201, )": 265273638532179914104433251928362073281, + "(200, )": 32426120593776790703611026267207377011, + "(199, )": 268960836840305717133043671175436776972, + "(198, )": 232904745369913379296581859438762420617, + "(197, )": 273087800308826582781491308322989757546, + "(196, )": 174092358488566277910529031766099429195, + "(195, )": 33560450555846031257723839033711568330, + "(194, )": 116778023869958136066492306174518360059, + "(193, )": 263864323502832331351594678784020852614, + "(192, )": 41726245417828680857265463960238918480, + "(191, )": 314879155946739932038433930805517326663, + "(190, )": 320042936143813043858692243949137123169, + "(189, )": 282517828152075630435738636246554631767, + "(188, )": 302047356785772266870725913001261053300, + "(187, )": 307050960177948339369278723426738978345, + "(186, )": 19606115762590340916934133609401168245, + "(185, )": 239968921648965596288061418858806081389, + "(184, )": 286080203825824879205022595803043435544, + "(183, )": 277455815107388523678161935704397356354, + "(182, )": 289456284251561597077886641947887781452, + "(181, )": 236200411360828589866305732835843337641, + "(180, )": 200126679513115392395211119863778400122, + "(179, )": 233544148966760622210715055770824794065, + "(178, )": 66390776772428784424202617604336561472, + "(177, )": 137944855099196360529422642560704960423, + "(176, )": 188630590367998313084449024415360715295, + "(175, )": 26105955154766218618764372550084535399, + "(174, )": 43008895139098406296496691379497723795, + "(173, )": 65716275928889955415161132026638182181, + "(172, )": 148929715511246784980941539198163129757, + "(171, )": 147704156298511298825766470184477630775, + "(170, )": 18283597095163623578073645746324110388, + "(169, )": 273230054581778561356978548312565836980, + "(168, )": 227960186836015636916013365019340597250, + "(167, )": 11844731582651924992281388506962742502, + "(166, )": 304722228023227867937592260503423843340, + "(165, )": 324166961835463912650863987671492249354, + "(164, )": 230250756268747529439620188458730643713, + "(163, )": 192738146868062913663135625832406305020, + "(162, )": 150692209824597048930566428231173251396, + "(161, )": 332002122322053252569173143433454891798, + "(160, )": 190222392044656910895125458320951900950, + "(159, )": 215113222317986064898914872780782710019, + "(158, )": 186289609815773044041229976275459322742, + "(157, )": 14688450978597956577009917270113392736, + "(156, )": 219950262739454099594390080651804536199, + "(155, )": 70442324067161192679959260817314661738, + "(154, )": 14521578992933396717859137096656636597, + "(153, )": 187202939647886860147217698008101740872, + "(152, )": 318901950570391480781263214668434859336, + "(151, )": 269082159585401611459725029027900535156, + "(150, )": 177990076861932019743373825976505030671, + "(149, )": 126205458538120590030134699318510296328, + "(148, )": 133109874930143151746201336168955347259, + "(147, )": 144603766829615669034975503515081838075, + "(146, )": 294048247211213547987152713054538312874, + "(145, )": 58735050718641366981477617083741514109, + "(144, )": 32360197257693611858606346951297865006, + "(143, )": 312125138692788834112383283484448732882, + "(142, )": 211244176819374316665029509940842437069, + "(141, )": 47699620654215390082675403983385178451, + "(140, )": 321460057871502638958270630237422087771, + "(139, )": 97817972562609242634204118164838487735, + "(138, )": 150869807292489536509771893657799388575, + "(137, )": 91127832664040375657129153785488880032, + "(136, )": 6633753825643563997119393292482321956, + "(135, )": 302751428530735504023893208725706792355, + "(134, )": 190889868132526079146984193292952604994, + "(133, )": 43118705798336925513284207456278436957, + "(132, )": 194808934351297541809928977538229053654, + "(131, )": 112188697735361389008678695298284147441, + "(130, )": 10963532341766883988604555961635397869, + "(129, )": 97532463226327542510100988098314699508, + "(128, )": 254219187159793965666907849606692214777, + "(127, )": 302596222256553380902371644916456660484, + "(126, )": 164025472903951469359757446392708821852, + "(125, )": 114339793729146671846503422976480968929, + "(124, )": 257615459008090665967916760316891754290, + "(123, )": 262634147631483153626846363409122414587, + "(122, )": 303514265406732356721903901235790125307, + "(121, )": 289540524047927345574132419821134879813, + "(120, )": 241953891345483882103139614550063157848, + "(119, )": 34169528629740875492988438001861842862, + "(118, )": 134399415526485301460273596687576485534, + "(117, )": 21397376181282874526810103072108650496, + "(116, )": 189453077708247680341397319814900170084, + "(115, )": 327690922917486335668890186183100963242, + "(114, )": 203078464493366114281849492911896470553, + "(113, )": 235873708567306885389864801084995031060, + "(112, )": 333777075148594349357714953750132521111, + "(111, )": 163988131174047445340711256735003018479, + "(110, )": 83186132694778952707946788337698512737, + "(109, )": 199813101146251569234020985083106269154, + "(108, )": 169107936005853405474566106246354449622, + "(107, )": 134932540588358152593251691040984704431, + "(106, )": 121118921195043956045070470595505073990, + "(105, )": 123305269160746163422632145429353451137, + "(104, )": 179868336583267367308134474806111992930, + "(103, )": 114541204054904537686570512993847974226, + "(102, )": 197932808728683278204361680389861917202, + "(101, )": 104752491972260361450979703271391533027, + "(100, )": 88443520317211227850712282722928273446, + "(99, )": 216911655648606741916725261803822286537, + "(98, )": 169506668489382950009450482051065806467, + "(97, )": 34869510953537361796056519525861879454, + "(96, )": 66544183879969579249245930183554600114, + "(95, )": 336346447311304670206328798125250196249, + "(94, )": 237032191002161084677315023075555336605, + "(93, )": 287892665035025545637291305563118147034, + "(92, )": 240107640913466537167660101611701853927, + "(91, )": 209353685187997171195561238880270762817, + "(90, )": 7438536877639807236588993445984634571, + "(89, )": 41320524485315179503463278973874060163, + "(88, )": 91746536211622659618097316391809711755, + "(87, )": 63972619298614113945459089316983807943, + "(86, )": 984522709974503403977768152357634866, + "(85, )": 71402564122835216529594714964809267698, + "(84, )": 111116030406692058849844237429371398475, + "(83, )": 194946735772834159844832121472346625144, + "(82, )": 108487827054913623512251205831110349391, + "(81, )": 304123193320099411981373450347306301336, + "(80, )": 320723845383538481667162779067238281915, + "(79, )": 105686971374844068442886757552282411873, + "(78, )": 261631993966157723664829282956396517025, + "(77, )": 47529895563658698363365625295606124035, + "(76, )": 278865374308950746116406441557441807761, + "(75, )": 240426568937586626571237265188788783303, + "(74, )": 254536817657888287153003371644389076543, + "(73, )": 174267066723157821719104308632655137249, + "(72, )": 203679008138183242385336834096299886997, + "(71, )": 59607754951118478971851140764244441511, + "(70, )": 288326857002692662260116585367342997457, + "(69, )": 213182232030686812418663528970408477418, + "(68, )": 211424969788048487754094602462654152494, + "(67, )": 6148052212015794136874050882436030435, + "(66, )": 36447409337381547070666465788420935605, + "(65, )": 268101014693254083177845407322127777577, + "(64, )": 236329552462300018901741603269631209203, + "(63, )": 70889824132317383877924306172635477287, + "(62, )": 134931006785290833401228553411642179549, + "(61, )": 91805369480546222593315462308070633000, + "(60, )": 285049111389301693088784016435037343186, + "(59, )": 284355421583204038350508460356499189371, + "(58, )": 164099431860615919427595182896829160952, + "(57, )": 43004500084810225014403706085469058485, + "(56, )": 181366628950560934618904437418648108951, + "(55, )": 298965558761706426076647215107753700733, + "(54, )": 245665194426593695186858948601959319099, + "(53, )": 234687016510352269183632591035888912928, + "(52, )": 326915818132144928165847054558321787043, + "(51, )": 220507440887040912420443647462853621953, + "(50, )": 313683602172495401614021454298380723874, + "(49, )": 320420473687847471877199209290837740196, + "(48, )": 137088730157715072810765964002548721039, + "(47, )": 292566087446004217736838014422415408403, + "(46, )": 13851265882638473006092645732188751151, + "(45, )": 269561289296377132712057887948699106323, + "(44, )": 244993470704932386411122785929399492404, + "(43, )": 226634301509318426311657915949961453796, + "(42, )": 20148073309556757548395833762314956483, + "(41, )": 172941378436426746523897153273163705561, + "(40, )": 262931983119369067211454126329459335518, + "(39, )": 117183102860347277104972968317774345010, + "(38, )": 252954510470541245717152704273472483022, + "(37, )": 31814071008674020389358792077809896902, + "(36, )": 127654085830845200708143346200828769505, + "(35, )": 107939655435687178157310639050911912337, + "(34, )": 217754234930238324324837870405351846749, + "(33, )": 52615808976052275416021235975355165781, + "(32, )": 179998465092732523358052639174061051344, + "(31, )": 168287898594547556940802780141616032152, + "(30, )": 319733474318367129922154895814648863827, + "(29, )": 128545141971115355150550961364385180330, + "(28, )": 327857437773299528710766935878990357644, + "(27, )": 147614693557171416535889204860182507099, + "(26, )": 271532460902211829118507794185121755152, + "(25, )": 300499290656897837231817222723760920792, + "(24, )": 214971060985098131877399997300663243523, + "(23, )": 41959705611205027431816030402444184952, + "(22, )": 76689803310003169109155303600056396412, + "(21, )": 318328377109345190352573738888873235499, + "(20, )": 247352352959260103058030322672277467001, + "(19, )": 210037187839704582642352751660479225285, + "(18, )": 20369401939280942907126149964685799603, + "(17, )": 7010068575083740630499072425493123181, + "(16, )": 245804699036556765535761273363457091500, + "(15, )": 311955739101991489011923174994266526294, + "(14, )": 52881402712572038120353808407988530198, + "(13, )": 320763428992920544142893048761918776957, + "(12, )": 317888090163202035775447874263956162233, + "(11, )": 160593640780900535439106939354897864256, + "(10, )": 320370959379109026316093943480983576915, + "(9, )": 70910939250118147735728438689984603005, + "(8, )": 134372213129697543244486653549126468882, + "(7, )": 94206994993471600674804815673240295684, + "(6, )": 290162137145063871765267160916706029691, + "(5, )": 189896206842760435839703210401323775344, + "(4, )": 283066512518775963647185018045792989150, + "(3, )": 191644774388994368495732509977025924922, + "(2, )": 902050147780858048170417023380844920, + "(1, )": 181871219391920767336363464300245387116, + "(0, )": 124157886448785963474673161320068328443 + } + ], + "my": [ + { + "(0, 1)": -125899823110384709916817034053899382079, + "(1, 1)": -20829863361241377950033827753675145008, + "(2, 1)": -50717256281754653505345386215503001551, + "(3, 1)": 136968133225875897510873910464011096202, + "(4, 1)": 76660194237824873028397011243826224139, + "(5, 1)": -29332567653680321825369303396788712831, + "(6, 1)": 159358857987890770964553097455351492239, + "(7, 1)": 81504791307080727910618790248926959881, + "(8, 1)": -125365447909893403276130349507649638155, + "(9, 1)": 75203138033811964044685154291214648074, + "(10, 1)": -36608453784804705011013361466862240651, + "(11, 1)": -157825338098648310983029568863403304532, + "(12, 1)": -64057237772216003087385593828069438439, + "(13, 1)": 110861083993660529555779377668806597938, + "(14, 1)": 49070432224218154201558851270994399741, + "(15, 1)": -12122101948564836744048155081859593456, + "(16, 1)": 1359592210798477496127727872902697725, + "(17, 1)": -57716715636214871996750142682686717051, + "(18, 1)": -96111370291742147956052805088286175722, + "(19, 1)": 166657323983210719661510604848456768153, + "(20, 1)": -61552959119587562756566854702005079738, + "(21, 1)": 112509960339117413046434074488841036744, + "(22, 1)": -60162261927389170938370352989465792488, + "(23, 1)": 123419160991938788527567075669046151272, + "(24, 1)": 38413185325635240655449254222573493758, + "(25, 1)": 293561145634436017079080562221280225, + "(26, 1)": -117172681534290964062951461602509799918, + "(27, 1)": -43591914884469782330856421747685883350, + "(28, 1)": -33781032105501632050939533649141872012, + "(29, 1)": -113447169326589023824330308933859933257, + "(30, 1)": -164988962077556457333192136445537843057, + "(31, 1)": -119846673132724010221072243951909475092, + "(32, 1)": -92509849824088865390388496751336193312, + "(33, 1)": 147501151850814344252472604158671198136, + "(34, 1)": -67151325575383263797278511810173866327, + "(35, 1)": -169073344151296557679343636585641418117, + "(36, 1)": 131291460285386919603872110664818515962, + "(37, 1)": -152611121181265670822681349059527316980, + "(38, 1)": -8124938787877948975979169547992997122, + "(39, 1)": -102537588503751321880447144004239519477, + "(40, 1)": 3820917686604299369177859721936455406, + "(41, 1)": -38169491504172777274453273190698254701, + "(42, 1)": -49054381979409427112856571083686777301, + "(43, 1)": -78750299244725253235717978607652940673, + "(44, 1)": -80699098669916402003499409295120656345, + "(45, 1)": 149097516355080433239140345529900956089, + "(46, 1)": -103718620963289485137457416397570136189, + "(47, 1)": -158556589263320740028306706115170916946, + "(48, 1)": 123312409658368183799004771803498316992, + "(49, 1)": -153529835899622944583596657054209126702, + "(50, 1)": -132535011478128176707382978303535410765, + "(51, 1)": 47672075800242978164085048691238311898, + "(52, 1)": -92351085670068628111520144057061008241, + "(53, 1)": 128999957372329962810096199332738592927, + "(54, 1)": 66213429377966079219318805455411115378, + "(55, 1)": 13642956913007050148987751720088675722, + "(56, 1)": 120956004481302134470672355632931994707, + "(57, 1)": 38047043049944253939928911936858964711, + "(58, 1)": -135512232251351249988484593343903843548, + "(59, 1)": -105471813135592171107305755272238154349, + "(60, 1)": 166523048220162944020801560878266123024, + "(61, 1)": 113542581490980454861983177330982786759, + "(62, 1)": 20135323607249786968300433092853365872, + "(63, 1)": 31080668714115238014831570494215436699, + "(64, 1)": 29187504089278004455619781200280515551, + "(65, 1)": -146050119489326261862602691925650914587, + "(66, 1)": -82286173831560749643375210648972707993, + "(67, 1)": -50840058606514445497735032834552740100, + "(68, 1)": 71856277811542977842197850751199188216, + "(69, 1)": -139401182261439141207147786360453338572, + "(70, 1)": -146974680886062988531880857961657357016, + "(71, 1)": -113256766434185825780481802428278061393, + "(72, 1)": 95299950850431009535012163205453852638, + "(73, 1)": 127795030407073905134867832580003243361, + "(74, 1)": 45290923981915161017305356426864568755, + "(75, 1)": -162836847191710466511168913109656932536, + "(76, 1)": -86886328814683589583663574418514475985, + "(77, 1)": 54955643006832915532680796722022877639, + "(78, 1)": 103777980174256399871495543778074895210, + "(79, 1)": 74698452863142907413226995708600030509, + "(80, 1)": -53149679069445561932006042929210885077, + "(81, 1)": -36763288043390866779934530179290420962, + "(82, 1)": 18227749188205474674702552247581196482, + "(83, 1)": 77648570462686380100579538062616198932, + "(84, 1)": 66043215754293457312622013900955314136, + "(85, 1)": -146156887782202771401715724747488220231, + "(86, 1)": 51190692210786060224880521151901349570, + "(87, 1)": -118384437982988923916309691763567322677, + "(88, 1)": 96666667912128252768474823222631713370, + "(89, 1)": 4154731628043907908995372087039514296, + "(90, 1)": -1958283289985440879055267683878351560, + "(91, 1)": -135655191727998225730047511469988446288, + "(92, 1)": -29864635683583436705884202669600988207, + "(93, 1)": 158959986148247851410594950472010773055, + "(94, 1)": -157506010390178494122070000400296797671, + "(95, 1)": -20616913843416718796392728018450563881, + "(96, 1)": -120672963360137398597099449487467645588, + "(97, 1)": -65180678881085522331485688163831202005, + "(98, 1)": 112601231782673613846288481889733304336, + "(99, 1)": -96981119584046357503166685104857578974, + "(100, 1)": 58038693425773846190826930111824769603, + "(101, 1)": -16733374180244274805835059782180261367, + "(102, 1)": 81292914154335519532249308080986940375, + "(103, 1)": -144153017451097622324523233860067470680, + "(104, 1)": -67040043046359455496084913755207961276, + "(105, 1)": 140212379642495162037143767838652286185, + "(106, 1)": 151741239378602274713545251543883998942, + "(107, 1)": 128158651826721683482139055183618469812, + "(108, 1)": -119916960949902262337670986964180846947, + "(109, 1)": 67298448138732608116925571441772448214, + "(110, 1)": 114137873998718610225714277792828269753, + "(111, 1)": 160938191473792526865045066549040224109, + "(112, 1)": -143869477023873877234195769005199676246, + "(113, 1)": -55464414876753413088688883923343419727, + "(114, 1)": -67394536534987908185669499425086826250, + "(115, 1)": -19468662692880146960292188998489396658, + "(116, 1)": -126048279426756942810204379551672448205, + "(117, 1)": -146068756888821888920579173137011298075, + "(118, 1)": 82088076693060255986040661752107077577, + "(119, 1)": 169842445914567910547116280576709840922, + "(120, 1)": 78754103372873388653089068898395464363, + "(121, 1)": -124765675145924177612682606883234999709, + "(122, 1)": 40474526400604368040557382465593544982, + "(123, 1)": -136936670910928498144353754695359690106, + "(124, 1)": -15744243290867531270167978729630008992, + "(125, 1)": -121350460871945937413383521862449919677, + "(126, 1)": 151428591486245263450537251249318974572, + "(127, 1)": 434979533890396236487720432271463612, + "(128, 1)": -7111366466078034485278893742143864173, + "(129, 1)": -74447207721669266615547520843171918684, + "(130, 1)": -28334874609857944755648838130027714640, + "(131, 1)": -142869152396976931774984389422444385891, + "(132, 1)": 140412708194038249342750102141860585154, + "(133, 1)": -64095325528340354979491515719796794588, + "(134, 1)": -163417441115545112567250098685166602950, + "(135, 1)": -141607217311012263090143807139065857295, + "(136, 1)": 31875858579701800834774254385318999906, + "(137, 1)": -113278723397319047151737900461313092382, + "(138, 1)": 89750818604859018557632372165021574837, + "(139, 1)": 84523628004501475829630788945145118720, + "(140, 1)": -107327782003030892073374818945095223682, + "(141, 1)": -110349459521594271616362528947455723346, + "(142, 1)": -59301867048846761294551186401642659304, + "(143, 1)": 57375341728397211332941264726432606079, + "(144, 1)": 69608362389625871761620687676337298402, + "(145, 1)": -133160184952959787475335326934010802134, + "(146, 1)": 19650973480398074877563166337307178074, + "(147, 1)": -52492701395034195207215501095694838798, + "(148, 1)": 24959251488776864307894162464762808419, + "(149, 1)": -166477067887625679695968888655895639766, + "(150, 1)": -8050902307224115913176195791669473629, + "(151, 1)": 161807572438904938632943909325483541030, + "(152, 1)": -65870379267104607150818278153113821185, + "(153, 1)": -5474629835437140760565101428342625940, + "(154, 1)": -55697306135740080074322562190145346934, + "(155, 1)": 23862210858974359341727233830094294498, + "(156, 1)": 165155707282372937458989321959544193224, + "(157, 1)": -84527720320315718225802107523257302654, + "(158, 1)": -158773079264601841620830117880435493700, + "(159, 1)": 166721586868453660597254341503389463292, + "(160, 1)": -14486275412956225725290936636989342675, + "(161, 1)": -91828294940912687142142739170649398544, + "(162, 1)": 1045389098320366497103570146828235199, + "(163, 1)": -91788704362191522320823481391984655849, + "(164, 1)": 73015303693743946240254970821060852212, + "(165, 1)": 13845534876421505370366433002280667920, + "(166, 1)": 43749785416822822422427511730207376775, + "(167, 1)": 19162320336296227198241882720797002975, + "(168, 1)": 54515419627999614999101736199571585235, + "(169, 1)": -153883371485329315985107716979938704687, + "(170, 1)": -164825573829893142123520529703859504984, + "(171, 1)": -95721153604517860457124731667043624065, + "(172, 1)": 18126956501861726451584440610949818368, + "(173, 1)": -130412237563979346026685104795721986092, + "(174, 1)": -83684992576066038286308675557816736837, + "(175, 1)": -82012365313796771365941326198768895658, + "(176, 1)": 156506033241184211365622845392655563150, + "(177, 1)": -115693804993966925938566923221113346219, + "(178, 1)": -48523471744224818175042519884819616973, + "(179, 1)": 136942654881185129655296797704142866726, + "(180, 1)": 42530370790636015552493327934737506875, + "(181, 1)": 115678510137359171491801895852046393407, + "(182, 1)": -17516113066135541554337800360227460643, + "(183, 1)": -148386245941823003253064141716125260995, + "(184, 1)": -24052732251264228220688017988296644830, + "(185, 1)": 21721391188427920989362121390468979415, + "(186, 1)": -55633372178073743322343379363267295356, + "(187, 1)": 50850501579448893420138849112530057940, + "(188, 1)": 122759526030224907399420783280533775959, + "(189, 1)": -72240759762078865034757623310438351580, + "(190, 1)": -75101564440173707567613006526695377356, + "(191, 1)": -94203257323401681270347340939647907560, + "(192, 1)": 130244072199098427079101866366079664896, + "(193, 1)": -46094298530722877862799444946492770110, + "(194, 1)": -89870638603148097200280368538443809376, + "(195, 1)": -134057864126833445272486476062580271091, + "(196, 1)": -59324047799703923390007788703009320008, + "(197, 1)": 94368341094917733944482970458578247544, + "(198, 1)": -86318174331865168829902562393359049661, + "(199, 1)": 136853519021613409622934689234865626713, + "(200, 1)": 126558062045907254173143732563921895304, + "(201, 1)": 83184813235904825325797579995410409239, + "(202, 1)": -97072869085172857137942536807741720663, + "(203, 1)": -39846490828020466536517771552121671958, + "(204, 1)": -100669202233238898711446176000051358639, + "(205, 1)": 30712180662960566853875136700343714486, + "(206, 1)": -79704499605624430146752712076052702257, + "(207, 1)": 78897550388803627232348798989212717928, + "(208, 1)": -112284207215866442227487695270066555689, + "(209, 1)": 159688636228594883417339276433105179007, + "(210, 1)": 121661012638810469253440703870580521772, + "(211, 1)": 168783531439943812413253820983827548464, + "(212, 1)": -63054941274833783949524486548127344732, + "(213, 1)": 66197933795669092074609903036729705573, + "(214, 1)": -37260193059379314877277065897364942530, + "(215, 1)": 42899762562077246813670275212567559913, + "(216, 1)": -156350301544343312301360541830491619915, + "(217, 1)": 98262560463551675010550071646887899431, + "(218, 1)": -41015026488459340558432750634296250234, + "(219, 1)": -112681188635427186256382977904958989372, + "(220, 1)": -101446964811550987082562637430671340741, + "(221, 1)": -36148672259515339574368861657030180005, + "(222, 1)": 73567564282601885028567267630922445335, + "(223, 1)": -162536316731464531554896759422625368225, + "(224, 1)": -12597128295515100894046232200728366676, + "(225, 1)": -16762351992765823711270566753141669367, + "(226, 1)": -40131107334147130509985239340975865648, + "(227, 1)": 26752546558493694446726826503101185111, + "(228, 1)": -59317561807509261887657173581973001288, + "(229, 1)": 156697419993738463021223799533119056877, + "(230, 1)": 29529907594517680588050549497008042162, + "(231, 1)": -48286482864312058263753960754352619817, + "(232, 1)": 50551160911754637959338132683220245323, + "(233, 1)": -157137069439392150302690398344033307868, + "(234, 1)": 87449040986596249206995291885091399616, + "(235, 1)": -156251065535343549665195118516052106952, + "(236, 1)": -96526234645134434828092836177129565885, + "(237, 1)": -147609980492758061186424399103660251814, + "(238, 1)": 45040455411233734383915610059670407595, + "(239, 1)": -164569344087430549150530228619245670020, + "(240, 1)": -158131478822499337039380791447179690754, + "(241, 1)": -150058580676407741458329249602442501042, + "(242, 1)": 150036118618107086809169150535067857729, + "(243, 1)": 25967367206387270336444265502670174674, + "(244, 1)": -164124237073994871301288563159052266875, + "(245, 1)": 144385463590440844105528816170725421943, + "(246, 1)": 151548650936307795283917865942848515600, + "(247, 1)": 93522334892028102413068518411664882765, + "(248, 1)": -158890935845215715532082288940778285357, + "(249, 1)": 91469228511663131566459617165140355487, + "(250, 1)": -129915568667790712651082756980397265587, + "(251, 1)": -50482774051966276100344603680192303647, + "(252, 1)": 20928973356097191488009602899798451561, + "(253, 1)": -124406931720601719571751654948182802225, + "(254, 1)": 23444755749737347631853531908893174519, + "(255, 1)": 111011112292980601715167326544924942933, + "(256, 1)": -41305838990806416116829701282656477308, + "(257, 1)": -167439592569969896789596471872846043575, + "(258, 1)": 54458237946988291367137876093558654374, + "(259, 1)": -62034218786482447035155610557997338283, + "(260, 1)": 21763548275115314459976800356898241583, + "(261, 1)": -98700947283510290588220608331443486268, + "(262, 1)": 103331648462446720403206471321885706792, + "(263, 1)": 83055057143747810499800408212743816088, + "(264, 1)": -58690828949470875484536717011178299444, + "(265, 1)": 6741766542373330386073460875930662546, + "(266, 1)": 110212770142452927273257974801094953504, + "(267, 1)": 51208684169220173276403652947592474640, + "(268, 1)": -79459380179203884677482914616272606788, + "(269, 1)": 148992155588702453712695748373926928816, + "(270, 1)": -161266902138141119641004890894566247736, + "(271, 1)": -44339510682073213814112452498741580168, + "(272, 1)": -41680413755228003252272553475551652131, + "(273, 1)": -24104481423313755819451614399459423268, + "(274, 1)": 100505858477671909461762628046451163167, + "(275, 1)": -83736062391823431543638987159252960228, + "(276, 1)": 54710917546370168102512002679073917063, + "(277, 1)": 110037339045076304782742365077282550677, + "(278, 1)": 127775569740176279106598944505158278924, + "(279, 1)": 131133296545726671608167341182211098225, + "(280, 1)": 93807626170375222993108788595335660358, + "(281, 1)": -135980779244828136457452875268909050327, + "(282, 1)": -71565352882208221172242623771169342330, + "(283, 1)": -150410466922733905789158799771835297105, + "(284, 1)": -17187713456928078600897898774674881542, + "(285, 1)": -30541092311623872837281173601491155072, + "(286, 1)": -83281892616069728369719695655409177429, + "(287, 1)": 20526538290450360846958206475383487513, + "(288, 1)": -35370949690292254809980011772025623226, + "(289, 1)": 85400979948372923497225257996607464182, + "(290, 1)": -59317109835694363388513496372254861923, + "(291, 1)": 161862863566128703703744408831468519051, + "(292, 1)": -131203091339933409396397258350092568330, + "(293, 1)": -80632925695611579902174654487761522761, + "(294, 1)": -106558810331092487443579145639407298562, + "(295, 1)": -106595663900625796970839174690391698459, + "(296, 1)": -101040735579304759634051774331228870800, + "(297, 1)": 168655632751179013173656662268690151163, + "(298, 1)": -134727467533503376455707988332154596202, + "(299, 1)": -98679314351889273606575604443838756849, + "(300, 1)": 24497282639593212630758116274315326345, + "(301, 1)": 24617220458859146667487692387935927886, + "(302, 1)": 129796558094194966947174628686704414731, + "(303, 1)": 165758536796322846734273161722556812174, + "(304, 1)": -20127226884597544984764058802547728555, + "(305, 1)": 83603297204424179314709254902008730780, + "(306, 1)": -91411017107065220489422906119793583085, + "(307, 1)": -72032277108760753888610775533489813683, + "(308, 1)": -150907281371204022028328237239552474090, + "(309, 1)": 69633112926091327285591627645030700013, + "(310, 1)": -22164327286870947173235253303883729272, + "(311, 1)": 7266290394535370437416773425975776200, + "(312, 1)": 147179844943491133182253979558376744356, + "(313, 1)": -96553345117096107109257340496468748442, + "(314, 1)": -19725930775913450241602803642986969296, + "(315, 1)": -913160482714007806792792744683864473, + "(316, 1)": 95452298060360570717882527221929906837, + "(317, 1)": -128460276867795256993256512403103998684, + "(318, 1)": 100346856177207859192211455671127261578, + "(319, 1)": 93991172720100550687575477258070196432, + "(320, 1)": -91300335826908875255767522925133420710, + "(321, 1)": -165687072187485099918060815044433615051, + "(322, 1)": 118697607799635402993696810683603311019, + "(323, 1)": -71038318450318409436258742229787791608, + "(324, 1)": -14965429818363573595444728186645980667, + "(325, 1)": -87650826043885646746006232681802608805, + "(326, 1)": -51806740932180458154513487874411843936, + "(327, 1)": 126497204717805836119674433949703948903, + "(328, 1)": 53833774911045714587717374052758631053, + "(329, 1)": 161390278494575929257465052371131888490, + "(330, 1)": -117599663974481301682912021727183837316, + "(331, 1)": -156958696413453222210299183239723596445, + "(332, 1)": -108464522783186636790874351673304961552, + "(333, 1)": -80686621862671018238427240281919271889, + "(334, 1)": -129833852935566379474455067525975989621, + "(335, 1)": -42963057045209717694279436717221566074, + "(336, 1)": -154309229006759225809525610215531463799, + "(337, 1)": -109782378956713284231009211897032788991, + "(338, 1)": -90017365724315775605802235848612768741, + "(339, 1)": -36346955998827831633478893931780379400, + "(340, 1)": 46590627626828834462972479697922459020, + "(341, 1)": -164539167212441359769616373585325977842, + "(342, 1)": 75771181427272680552701139280278995150, + "(343, 1)": -81068368459700316517802553443087289781, + "(344, 1)": 86276199334152745604793013993972757899, + "(345, 1)": -152949450451132655557729509788943431144, + "(346, 1)": 168310393960293224059137092563971802972, + "(347, 1)": -78264588414872664684979726220342930686, + "(348, 1)": 168217500982954079147391314570178509702, + "(349, 1)": 128718598816991855998773081432334273125, + "(350, 1)": -149161477250525842404701104917105399869, + "(351, 1)": 151432053772683899061660952840942746092, + "(352, 1)": -155317215607597775659874360328133891513, + "(353, 1)": 152798216501348855892304820498330062424, + "(354, 1)": -45388580994229545217567135235141975434, + "(355, 1)": -67966297873169995218750046477242230704, + "(356, 1)": 79244085004682456334701739544579633518, + "(357, 1)": -47503190592680453425132835857854495227, + "(358, 1)": -14267300915148016589550938154801968121, + "(359, 1)": -43377251732869956267966304558780886385, + "(360, 1)": 77184906037964454521452131149297078806, + "(361, 1)": 1082520427137866753361656650651503557, + "(362, 1)": 9787952850566662599795005784389261500, + "(363, 1)": -164205115810726266745010038252866834815, + "(364, 1)": 56698789171992560439382151201011648190, + "(365, 1)": -87700058168218351909273696628048328275, + "(366, 1)": 39471056393500243928980470321539965513, + "(367, 1)": -151036701769364905857806143070028895908, + "(368, 1)": -143326629572397478760803438424691673135, + "(369, 1)": -78767689668015362570199004140656411319, + "(370, 1)": -129541290480519818861431817537596309242, + "(371, 1)": 91291750553667611253091455988290640921, + "(372, 1)": 54699985594747148791893728819898938117, + "(373, 1)": 113643632245977725783370956902318919904, + "(374, 1)": 134456589532019718015529402183328227078, + "(375, 1)": -167350078670414237252259923256626160347, + "(376, 1)": -125729879377891461665852874247298387023, + "(377, 1)": -321315235091087058521524156812376603, + "(378, 1)": 124754730745516972491517317077646757211, + "(379, 1)": 29584433836736461421142505464151100914, + "(380, 1)": -40390958425292457965865169942110940697, + "(381, 1)": 136073545197312895282503388618881193006, + "(382, 1)": -155498482834477566749436491122449357811, + "(383, 1)": -82504512250951664394919017982033198906, + "(384, 1)": 31774208681498773950525620519874807455, + "(385, 1)": -146483410833699190262386772533312049407, + "(386, 1)": 52308942002071498714355728973063079201, + "(387, 1)": 87208870131331151747848619420671299095, + "(388, 1)": -86081072242756439799008435678702953655, + "(389, 1)": -93314864714275069917186129614652315812, + "(390, 1)": 158330045702855360103660232603423755597, + "(391, 1)": 24580438175636411955863288594088685633, + "(392, 1)": -47082418308086498855861185979364317767, + "(393, 1)": -38695555949568169614583308779120399361, + "(394, 1)": -27934999137833093405167182415569753622, + "(395, 1)": -154370599893583405882793235436528782427, + "(396, 1)": -165182219990740843181458685349190053391, + "(397, 1)": 144328595265862791720744978291243892259, + "(398, 1)": 12182002477311017773841291518478124797, + "(399, 1)": -134262471699634885976999540848853989518, + "(400, 1)": -9256925921968081568659266617106222903, + "(401, 1)": 122372366281995316533956797262770322627, + "(402, 1)": 79905668027589034379419176207237229336, + "(403, 1)": -56863595446920637323153578624906930061, + "(404, 1)": -144317271644110012315203505387194694473, + "(405, 1)": 64615737639321062323234973759271926583, + "(406, 1)": -116539441060160058471528980741493527807, + "(407, 1)": 89922525272354145971942081360053519685, + "(408, 1)": 47687390247259558863307670141854237594, + "(409, 1)": 65145161066808615270171712733534869384, + "(410, 1)": 139517528019832211465836396226925128170, + "(411, 1)": 135717492315049428492638951987536566349, + "(412, 1)": -127222735119693736110993192099538961259, + "(413, 1)": 101663377979511048053397966508965060122, + "(414, 1)": 7868497976135009661555432085205563384, + "(415, 1)": -151616956785949854708950462379231117915, + "(416, 1)": 166186774817051696879706677024079717147, + "(417, 1)": 89884871059198892989673516413635555627, + "(418, 1)": 20920924683959418372591054196411373450, + "(419, 1)": 74499207163524283518465648586571592475, + "(420, 1)": -26807830470543509136359865110816336663, + "(421, 1)": -15883858845963974044389859493709947416, + "(422, 1)": 5811128337769015154133211498617534069, + "(423, 1)": 137664392916648250406853334936337204707, + "(424, 1)": -143972601332876783853685958658966333171, + "(425, 1)": -105925805649898952055247021847076522494, + "(426, 1)": 165942019156161800326828811836095756641, + "(427, 1)": -140793899621626277820101149496962539328, + "(428, 1)": 88233196878625682028556442291792263082, + "(429, 1)": 81811856398108266674850326503970325764, + "(430, 1)": -140101345544981000897388655143967212558, + "(431, 1)": 140730300566137019895397074043688573664, + "(432, 1)": 101583281028122957146656639170377396030, + "(433, 1)": -39662187303436979712637448686877861408, + "(434, 1)": -34370239012825166501542517869120665314, + "(435, 1)": -893370639835456805148612824774348339, + "(436, 1)": -113803119447500536957459329096384366247, + "(437, 1)": 692616813070710083560711373231327798, + "(438, 1)": 66314773112668968199466163664863571290, + "(439, 1)": 70756567417617956571010945854706604894, + "(440, 1)": -1031913323835851156378578544126142158, + "(441, 1)": 37972791968482540094183440202757773936, + "(442, 1)": -35161058920878326281732849761041524460, + "(443, 1)": -158280136296053098249864557356795018015, + "(444, 1)": -29078240708887863397922853145773478183, + "(445, 1)": -42131595742356785953670121065009603103, + "(446, 1)": 129742160306833884852258895353111931076, + "(447, 1)": 68965545673761089040414061361303859830, + "(448, 1)": 119824879169081002546506259412180550448, + "(449, 1)": -155366789780035282078231268019217837492, + "(450, 1)": -8388802048831311760571637907277724470, + "(451, 1)": 24668846429103839766715406304038951693, + "(452, 1)": -130590766327670753232192430933196997671, + "(453, 1)": 22449804108231293994030442631851259096, + "(454, 1)": 4827553698956397294273196420150391963, + "(455, 1)": 113308191781631257569160362435355385699, + "(456, 1)": -34875560132095689518091218660243346970, + "(457, 1)": -106847805320508791083650308924000397487, + "(458, 1)": -63329221349116138198617880093067061508, + "(459, 1)": -133219430577319167113943261226263974092, + "(460, 1)": 53390949315396761847372091069390058401, + "(461, 1)": 10710274354971972456927369861994542699, + "(462, 1)": -118395212250147521756563641829013627706, + "(463, 1)": 53856870385685418074088912742142974409, + "(464, 1)": -66984724979535218956449799848378152972, + "(465, 1)": 13622052363042670723574942435439474405, + "(466, 1)": -148173969536996912668654484066596503908, + "(467, 1)": -162310857357480260377755654708115230626, + "(468, 1)": 21986174183696231242156996020952563719, + "(469, 1)": -77169664114247570106200104113603098858, + "(470, 1)": 134538296195689941314160826314170630324, + "(471, 1)": 24451273755841684922418252412983756930, + "(472, 1)": 140691058551398525203130654945899933515, + "(473, 1)": 85419173074476178765489340423067477932, + "(474, 1)": 152191700510794356842372343640841528159, + "(475, 1)": 610638944037692163935718491871400449, + "(476, 1)": -55403055366013565071321043834292676242, + "(477, 1)": 3745316706562384876246988768652394998, + "(478, 1)": -31423328653136088407989605655980842306, + "(479, 1)": -147207366518966658928916414592583743967, + "(480, 1)": -116468554930152563822453841953673498776, + "(481, 1)": -15205193448593680912439696237011242168, + "(482, 1)": -96989183603755166889380909086111794606, + "(483, 1)": 44805144207684297735800843613711746480, + "(484, 1)": 114646087769372253125379062910428990671, + "(485, 1)": -104030705607210892589828900773982999233, + "(486, 1)": -122876798469375787181497723996700277834, + "(487, 1)": -39664716565705351773000255754542550304, + "(488, 1)": -102550992565851977462827139724881265380, + "(489, 1)": -71101572682458369630902702997002901077, + "(490, 1)": 77923579904063483454661547727252163670, + "(491, 1)": -86708171664958221945092610770091712972, + "(492, 1)": -124424815503876207487801564955469236111, + "(493, 1)": 77241285232465822491147761235603375758, + "(494, 1)": -161604801284718875743382795795882714896, + "(495, 1)": 28019273313697026463138196644924234188, + "(496, 1)": -170043707293617850104316579838417033916, + "(497, 1)": -51570134582303148956804367188235348739, + "(498, 1)": -18196741429457924307575442062957968438, + "(499, 1)": -31551180934650373764087297516590737432, + "(500, 1)": -131130749055039290034370211577403980246, + "(501, 1)": -102580810524337299216428207702528539451, + "(502, 1)": 117896416766630630026274202911843214972, + "(503, 1)": 54175433542802665448794444874818666386, + "(504, 1)": -36088172645509539970915744027914228537, + "(505, 1)": -12713146518712354460384459591803638878, + "(506, 1)": 122681359852607296337009254351380084021, + "(507, 1)": -73703823920086068792099593019070422801, + "(508, 1)": 30600435444403993109547621443356009033, + "(509, 1)": 96751188953374944151293635990150065298, + "(510, 1)": -120544097098075909174467388954572159452, + "(511, 1)": -99465795352392338048833533817306765117, + "(512, 1)": -134576045335113654909357267008371803023, + "(513, 1)": 13371112045989779932278476268747876797, + "(514, 1)": 1143454139157786947942204976582286234, + "(515, 1)": 70130283775492459147168849098494347408, + "(516, 1)": 66638030720066684194289454242482072458, + "(517, 1)": 5243348354499693930217221724538821243, + "(518, 1)": 117247862158586361052387914851240416757, + "(519, 1)": -129011149404617679540019364827708527014, + "(520, 1)": 145217338892182814756581185369674375885, + "(521, 1)": 101158560132377016671530665616485691849, + "(522, 1)": 114162741831944531419404373633287709757, + "(523, 1)": 23587474269336965270811090662649174210, + "(524, 1)": -105097938267228177684712429237321796042, + "(525, 1)": 13283376952342220469470201514311066136, + "(526, 1)": 21549303334214981796975035700612474861, + "(527, 1)": 164810938630500081057339164483838658451, + "(528, 1)": 55259798119333588724425968983282119300, + "(529, 1)": -49682155961081473817272271635898181793, + "(530, 1)": 90660945537886431670958822213954616862, + "(531, 1)": 54425285172000906114935314936429309561, + "(532, 1)": -131407880092085471944277465620096920274, + "(533, 1)": 95952655006933236249759174392614979046, + "(534, 1)": 95748603220523290647294628678223575878, + "(535, 1)": 23540062298292011664491772687014288904, + "(536, 1)": 81542834920072406794987750465505831714, + "(537, 1)": -71874183944225702266276320660292288231, + "(538, 1)": -23125942046237999474859671882185923253, + "(539, 1)": -75314766628976181584032963598154951959, + "(540, 1)": -115699644903765045609087022215214560944, + "(541, 1)": -45020120224849112256694533029712825161, + "(542, 1)": -129348850334994493480495101462595722800, + "(543, 1)": -60823037157243624855248296056682386173, + "(544, 1)": 127086259597383271723213858032864467469, + "(545, 1)": 5082506291590337163025508699893909336, + "(546, 1)": 141830557443299490088749658877612089389, + "(547, 1)": 93461421302644623140592274491404359679, + "(548, 1)": 13458294375861458525456729027127235577, + "(549, 1)": -45579082481283952126196176061062781257, + "(550, 1)": -74695789678673995595909314220338325303, + "(551, 1)": -28255945525966978450466075514174009312, + "(552, 1)": 130855706454976532213782988262649922291, + "(553, 1)": 107283637384022061964884997952854462304, + "(554, 1)": -63705175384125558709389747378220519621, + "(555, 1)": -115736482559843822238494185392561641969, + "(556, 1)": 24740401086329198355440162930616123428, + "(557, 1)": 48394818724209804822634298533401791632, + "(558, 1)": 79238554552704321773788815164656159554, + "(559, 1)": -24303431666757436122271368363070974241, + "(560, 1)": -34980023648843722939538070755354962958, + "(561, 1)": 150633181607244824761453305150822082859, + "(562, 1)": -87721602483234747273819082466428913255, + "(563, 1)": -25072560233679355811350914858461190445, + "(564, 1)": 42527629958407557176728313956098099653, + "(565, 1)": 63590733861471027141913798961312992618, + "(566, 1)": -7372119023836733023898247541838224559, + "(567, 1)": 130997000725546010155557539398578465836, + "(568, 1)": 149439453257172092473841111337865721981, + "(569, 1)": -166941975761988302471344511033226391258, + "(570, 1)": 167805003299363321179287970951625489818, + "(571, 1)": 83866914606690445735868774155950378635, + "(572, 1)": 150690704742626460902043314335039004534, + "(573, 1)": 58669033348378391452388414769751623999, + "(574, 1)": 40559968286990171155015618359055704689, + "(575, 1)": -167022404258060317119206202291763678163, + "(576, 1)": -39000605592674768744084222902533183455, + "(577, 1)": -81649955086881702020520959046711402766, + "(578, 1)": 18194249406908557415755237999032828332, + "(579, 1)": -71171794660931710769971156509835411506, + "(580, 1)": 120473496548195373179690972283496852943, + "(581, 1)": 78689338433948302360103418589220695089, + "(582, 1)": -112348981530325547554318584458977145705, + "(583, 1)": 101155854286958273026199602425857406651, + "(584, 1)": 135892441118592091819205656771204523957, + "(585, 1)": 101939033491347968123945864388249953186, + "(586, 1)": -129153478068262916742346725952166481134, + "(587, 1)": -53882151274965288327357679754672552625, + "(588, 1)": -18611077535798982431224934846475952488, + "(589, 1)": 45828826852854711325276461765930545318, + "(590, 1)": 99078931047148978475234230010054549363, + "(591, 1)": -72227270839926937539390130269124760462, + "(592, 1)": 54860632767714163073592137154139261039, + "(593, 1)": -27712631670755442665766996447381026797, + "(594, 1)": -81082738602404599334502652096171918926, + "(595, 1)": -19783873983818129716204071194855574370, + "(596, 1)": 26977726970990236778899947339052676486, + "(597, 1)": -145902312809019869540883869553670237911, + "(598, 1)": -164755999576785782349282980436336791737, + "(599, 1)": -85246863667243818204557795565250903445, + "(600, 1)": 35833041273485580214917867593453596417, + "(601, 1)": -23289931001851519658143037841923272943, + "(602, 1)": -118892438833526889792129306515717448346, + "(603, 1)": -114638981542036780079812588545897887720, + "(604, 1)": 154513648974099251243607090929139292068, + "(605, 1)": -169644453190837042385068852007266342750, + "(606, 1)": 148878641776096342602739225620054029686, + "(607, 1)": 18089501449407656900426001993154229818, + "(608, 1)": 52932261414013058241140388259395513637, + "(609, 1)": -124145869645781304640477404751520307075, + "(610, 1)": -124217567854919673125050716906487766836, + "(611, 1)": 430124331875484255101295364538710898, + "(612, 1)": -106700111178202286236413527517990084364, + "(613, 1)": 99160568582399540224563262080685803202, + "(614, 1)": 26568223262173920527635752334868351629, + "(615, 1)": -117087154673930471207394993145914065294, + "(616, 1)": -170031756217935276364672833218631204331, + "(617, 1)": -148185564687955500636663026337993078535, + "(618, 1)": -41440362888321317642767841915323622396, + "(619, 1)": -6099393139789265036085637677902208697, + "(620, 1)": 52426129539013349541541122175675201179, + "(621, 1)": -8787418006297473429696478442919481826, + "(622, 1)": 102053306807724185583054786826526808160, + "(623, 1)": -167426120525075877922288375420494950451, + "(624, 1)": 17898698260552152396005855386637089127, + "(625, 1)": -89460100852385988157861720545234780585, + "(626, 1)": -170039997045875274970113037681631963093, + "(627, 1)": 167021585074720949341283105909424955, + "(628, 1)": 31271818271706093176019068873069851683, + "(629, 1)": 128821306870256591766006091113070979695, + "(630, 1)": -50393455200884896682969053146322058740, + "(631, 1)": 64361748489962141001108119091568706222, + "(632, 1)": -129600076834564138727499220755349267925, + "(633, 1)": 134182124536149908010963320007428632706, + "(634, 1)": -158515650138250305691165911520030119318, + "(635, 1)": -97279650229821057859279319014179496801, + "(636, 1)": -51577692744158028015976877800953828799, + "(637, 1)": -128296034850894557278378391433263309891, + "(638, 1)": -142425098123253575434748677756976641479, + "(639, 1)": 161468648341907224183925080786028240383, + "(640, 1)": -164569206351284620773334175633719051534, + "(641, 1)": 84110030670905212085776996847702459767, + "(642, 1)": 86835176552614297426998218863346018844, + "(643, 1)": 48105608258512480297487053730361072942, + "(644, 1)": -68681206810400967492075748231769168875, + "(645, 1)": -19394473449921841039345821262647843830, + "(646, 1)": 115390228736495972500205659236849472883, + "(647, 1)": -55942389147288234881002461545531836893, + "(648, 1)": 87577040139130615846780835050039762930, + "(649, 1)": 39688321643471480952244334864844371131, + "(650, 1)": 131767211934499774720575471441854703322, + "(651, 1)": 128196020754688843327115404061379390160, + "(652, 1)": 33647226043432054385651442782863648659, + "(653, 1)": -158678832015943158122009928959790100604, + "(654, 1)": -52549332890891462393960317779190944636, + "(655, 1)": -77907207520398043849721255897545343808, + "(656, 1)": -73889757005295627048501082237139352409, + "(657, 1)": 112477782331116624439534376470193279056, + "(658, 1)": 159881312661066236243594697105969186352, + "(659, 1)": 12946873704030947960680182666184552122, + "(660, 1)": 52335033000061757956204983414896700468, + "(661, 1)": -63842046931959563931735809104504265279, + "(662, 1)": -53825431136670274970976769609796618305, + "(663, 1)": -53055796518328480135071437301123454601, + "(664, 1)": -162439112378905320376519416415928797533, + "(665, 1)": -64056321333047649928474477705287083380, + "(666, 1)": 165306727883216797766740217146289171585, + "(667, 1)": -234440190916190434508738970058165549, + "(668, 1)": 160454242055589554153179999614530884232, + "(669, 1)": -147847442603388777977864179192535061737, + "(670, 1)": 35930495042180617945847370229983140861, + "(671, 1)": -61650909731728372929853018765092981498, + "(672, 1)": -72562768956976445848496462623256549688, + "(673, 1)": -131317512980126984699961094054411526198, + "(674, 1)": -35747830101370840870351795034907157824, + "(675, 1)": 75492123385717552112656262848357889442, + "(676, 1)": 58245060129239015113769158755545692039, + "(677, 1)": -107996637806412005819155004214204042738, + "(678, 1)": 85483011311983361249167642105401256204, + "(679, 1)": 134070688567521523590758983924242341701, + "(680, 1)": -103086000087594810474245393488233341470, + "(681, 1)": -26543614019544208555298324610767926223, + "(682, 1)": -98044044988922395331529132575951674067, + "(683, 1)": -159446553459662330166614482245958228069, + "(684, 1)": 1467707201661392934485265760786345681, + "(685, 1)": 12471204661731504143092656249886572962, + "(686, 1)": 149266094092445359665197753295812515990, + "(687, 1)": -17348289969405396176598991950812944293, + "(688, 1)": 110117150207826451478678443065239616508, + "(689, 1)": -56794296953871404898707682347528716975, + "(690, 1)": 8559983058539282265352266984442028216, + "(691, 1)": 113541041180754290059116926250073711833, + "(692, 1)": -62321054196346855849394637822711053507, + "(693, 1)": -64221134328945603332125802516072604318, + "(694, 1)": 152507535653542731217724775070090241522, + "(695, 1)": 62534546742725232414606171646212481546, + "(696, 1)": 22465806697083950328389265726598110876, + "(697, 1)": 156432727220451267722276749560105970924, + "(698, 1)": -92752723157826997783230786287489279153, + "(699, 1)": -46495543809802567513494105893684628846, + "(700, 1)": -120507985902879616935042509710575311545, + "(701, 1)": -86735981604050899071958173684968900248, + "(702, 1)": -27148387599689898850920695406099474293, + "(703, 1)": -17208749080070526466195881295637989974, + "(704, 1)": 122286733807880765830012064170791474191, + "(705, 1)": -113141943344880142078301718878152932761, + "(706, 1)": 39442206961702265323099565304916580022, + "(707, 1)": -76968907354010099081312167734446856762, + "(708, 1)": 116986417356712276136924144615545299417, + "(709, 1)": -24433066561648368786006267139173889121, + "(710, 1)": 25080328219924458080803384242390122119, + "(711, 1)": -146715740631149727912342980882159521838, + "(712, 1)": -79882484658650232134522253570001178716, + "(713, 1)": 158583329759641319830317067512756274741, + "(714, 1)": -68814659036355746015781972270730392314, + "(715, 1)": 42041705711558775534892327208044159280, + "(716, 1)": -122469576508736578601929261277825813888, + "(717, 1)": 93856135315054351969321683142408590198, + "(718, 1)": -58327495279185676540502371278753430292, + "(719, 1)": 144352967437113149674082653205831495661, + "(720, 1)": -142074391190277215017118500489944963401, + "(721, 1)": 30573630562107066262123795966181344968, + "(722, 1)": -121930249431935033847566612403356839864, + "(723, 1)": 25156970053157527532567952439025071951, + "(724, 1)": 126409218806292330611802551163103182866, + "(725, 1)": -116785342535606628407392623902451079177, + "(726, 1)": 61589222194126740344362028242708479886, + "(727, 1)": -96429427159773868592883549721211985688, + "(728, 1)": 103320415636180493127626593225343113061, + "(729, 1)": 60314056891052456585337453993335121733, + "(730, 1)": 73072403829804076861110309131400191991, + "(731, 1)": 96289790316597021412661549833612599409, + "(732, 1)": -101116045388973629961371073018697960700, + "(733, 1)": 45252779228424801182547054651542977954, + "(734, 1)": 113475533601508681525371585797963533688, + "(735, 1)": -146558717295106469720763973408558144078, + "(736, 1)": -111366497122078232433515239785181079651, + "(737, 1)": -1690946316936344673053082278985877924, + "(738, 1)": -73315509097753734902222591290357222507, + "(739, 1)": 132839691731940086584090886121731311537, + "(740, 1)": -66661734799875736276914388452139409866, + "(741, 1)": -130482275390319995870361488582951707849, + "(742, 1)": -11158073885940079668418568508712905631, + "(743, 1)": -84593144544974460902666940120338844045, + "(744, 1)": 78222981418702774514160450182963627628, + "(745, 1)": 991322447247355532346275162764147644, + "(746, 1)": -90381058748185315819986718740643357161, + "(747, 1)": 24058224138718123573334592303269690975, + "(748, 1)": 127106101254212316071120327881415058516, + "(749, 1)": -99498542340857459222319858008770368849, + "(750, 1)": 99567354671370868118001199324842306573, + "(751, 1)": -15328930934381796070177774377203143258, + "(752, 1)": -6734078315591005925222657150892625583, + "(753, 1)": 103542732865066317310404599685401457870, + "(754, 1)": -143346380819996596759663795487866087439, + "(755, 1)": -162990001735490559417944559686584803169, + "(756, 1)": -82770842955189113090577076425776835450, + "(757, 1)": -131434159816797770849296148013047659481, + "(758, 1)": -120577991665719976536382403341211562420, + "(759, 1)": 167401801373994182725232918985445719880, + "(760, 1)": -78710658070600432376128015036381983286, + "(761, 1)": 16333013553294333546923472730669069766, + "(762, 1)": -112422458816887716314583520498361372848, + "(763, 1)": -117908639625223879286721475660732122749, + "(764, 1)": -160000533194778609967192245199549458229, + "(765, 1)": 120880069058727155042653933826244036678, + "(766, 1)": -138593914304635147003364474883865874423, + "(767, 1)": -156782522045601303864263281759479678840, + "(768, 1)": 77766363103040572763368894967782309233, + "(769, 1)": -145369190090649076920958416345539399623, + "(770, 1)": -153590811051703886562628305124858869292, + "(771, 1)": -154943039454612260605552983205528154534, + "(772, 1)": 112656551139681557825567640280410498714, + "(773, 1)": 166325165263706734668590705688184654587, + "(774, 1)": -160453446718103894273472703955311446291, + "(775, 1)": -144262718021481529371309735338762436041, + "(776, 1)": -77079745117916220890337934225008116820, + "(777, 1)": 14614822101524130727366396874276316112, + "(778, 1)": -155362507659280223822886009165815306189, + "(779, 1)": 27644776179579822526085926048559352832, + "(780, 1)": 123494748155323378559604261033768738408, + "(781, 1)": -162939717689731853775934106983245902764, + "(782, 1)": -17124241337125070607014315782737047089, + "(783, 1)": -13312722437888575973732021851851865919, + "(784, 1)": -55290289842173880920138882416134825457, + "(785, 1)": -153992124176631077588647379554343653044, + "(786, 1)": -48378213292356052792591488745748776309, + "(787, 1)": -104140112851624292057262899612737970362, + "(788, 1)": -103388367044707482154063390169821542820, + "(789, 1)": 89210353659162792197214917219846136507, + "(790, 1)": -99673176191734129644119915711115833403, + "(791, 1)": 10908275096608821807421654135117693644, + "(792, 1)": 89376661973644260263054911291401884625, + "(793, 1)": 62846771512812552784342165848925511747, + "(794, 1)": -64019789725351272521438907328224548325, + "(795, 1)": -75028934724359929817003793446653966837, + "(796, 1)": -83943758147496086895504334229460025646, + "(797, 1)": 11560273744584749489653643797193342789, + "(798, 1)": 106375341639107582190495388703563389859, + "(799, 1)": -118458793622103017445986613776893819362, + "(800, 1)": -166253543189201312486653922931075630771, + "(801, 1)": 31941239127612727861793107459709369901, + "(802, 1)": -72061634460342638144793956577550801289, + "(803, 1)": 138934167556951915716843010560823717903, + "(804, 1)": -111639593318768221255439045551999221646, + "(805, 1)": -129735730947318886088101711402443555686, + "(806, 1)": 105497669099788819245114410581617780526, + "(807, 1)": -152842041596373702686815527881681265440, + "(808, 1)": 3517937536369099710795167965355589448, + "(809, 1)": 73516427867479333973859273049877207299, + "(810, 1)": 116777506338619171585371886889182389869, + "(811, 1)": -127138409938161110754659997999154713382, + "(812, 1)": 124713799102985910960476363118400848159, + "(813, 1)": -89523736693015943064638452640250707152, + "(814, 1)": 75931773829653130615194937067268584862, + "(815, 1)": 16918116366737737267148207647849469958, + "(816, 1)": -7084974555808645919992390838291988101, + "(817, 1)": 67693276756431159095772937961658636788, + "(818, 1)": -113922871442338635409288027063188498721, + "(819, 1)": -108295309854965531897620943959020506443, + "(820, 1)": 82481878340862221293611813596162468277, + "(821, 1)": -105955319833960942278320318026007193848, + "(822, 1)": -118319711999998750793176394105288685978, + "(823, 1)": -11786370468091904959836629830504975757, + "(824, 1)": 112280033128517974517826469346977477840, + "(825, 1)": 16528687864639660732004361564347685759, + "(826, 1)": -114515334446171004312391160767877908012, + "(827, 1)": -105756290960459188510500072295670774456, + "(828, 1)": -92235908558690098088298592867292000219, + "(829, 1)": 141297455499041029789685074110204090820, + "(830, 1)": 93829170075516274952719218685064922284, + "(831, 1)": -99602212108307616894496055206591898052, + "(832, 1)": 43279070902794449847530919459393355646, + "(833, 1)": 120354902403878832469682309629853806068, + "(834, 1)": 66462310294044634535579258326921228050, + "(835, 1)": 114919779438445963328722264114257472396, + "(836, 1)": -24389129768128073924093861911327495877, + "(837, 1)": 51376086971832110639006653195629016500, + "(838, 1)": -34233565154378213114682554014927295925, + "(839, 1)": 20214643722888440839482136968061701789, + "(840, 1)": -63018999839386589109123364805385072709, + "(841, 1)": -159900607341240748386516665573795295878, + "(842, 1)": 157696233937897868119287946393622606266, + "(843, 1)": -75810045765900983172016613454925069165, + "(844, 1)": 8486228977366310752652059467599238241, + "(845, 1)": -72807809056999357839761720017549501086, + "(846, 1)": -61581751014287679720168775328838720433, + "(847, 1)": -18321327934483933620493596514616305920, + "(848, 1)": -23128290728778036884546553940913695895, + "(849, 1)": -27346263111167325919227730403584737954, + "(850, 1)": 96898795497459141362862512741201287907, + "(851, 1)": -119308143567132350285351008393335669798, + "(852, 1)": -26697175206047057686336512604909047743, + "(853, 1)": 56488223143221060382862258539052664854, + "(854, 1)": 168179880533796889842583524123717830848, + "(855, 1)": -30812145535524129553866952971903036744, + "(856, 1)": 89126078373695233297340011668061820312, + "(857, 1)": -71048378566637706763881732678484883039, + "(858, 1)": 120679879674063600468479893462914006105, + "(859, 1)": 54053880652827953901603937964042977295, + "(860, 1)": 4447225394587902705616459149196979697, + "(861, 1)": 32206786369015423531466688257987838002, + "(862, 1)": -82018087618710762708618711456281165272, + "(863, 1)": -39576707206139324756440429323916731776, + "(864, 1)": 69796372624342192210262653884691033920, + "(865, 1)": 76140833113865661145990649920938994633, + "(866, 1)": -90623482032204582663041705922920609726, + "(867, 1)": -1519366322028093878202190228149461443, + "(868, 1)": -48706013694401797142747291460231131670, + "(869, 1)": 54427692555316632041902876822591128120, + "(870, 1)": -118927075457685052358803804547857519960, + "(871, 1)": 65070482151612461931710666945703810684, + "(872, 1)": -16885709027349925678622835906294470459, + "(873, 1)": -18464954095538479581690018706515560831, + "(874, 1)": -155942468263261924521973975166113639541, + "(875, 1)": 4552388678540145958223564255505775726, + "(876, 1)": -7564898896632, + "(877, 1)": 48146352500800972717803036602767724333, + "(878, 1)": -137613168, + "(880, 1)": 882 + }, + { + "(0, 0)": -95215135865853493651647305235364260575, + "(1, 0)": -69635163521113154710271086021233208296, + "(2, 0)": -118803209435111174683154662326281922969, + "(3, 0)": 96668159617770178930857741369498019516, + "(4, 0)": -48015887854112646506847760221320362646, + "(5, 0)": 3754056944107361441033204328762931230, + "(6, 0)": 82491554046824940892817127504494894760, + "(7, 0)": 92255580431081344614480260506666584583, + "(8, 0)": -2481687478181546889868113945893987173, + "(9, 0)": 121895569215947688539958255223418580083, + "(10, 0)": 76948476783569360342289679186709185824, + "(11, 0)": -38001117283237769823477923244574737195, + "(12, 0)": 85554992757936551368378140984137554741, + "(13, 0)": -124225171862218359371723807726983756701, + "(14, 0)": 66977634606066537949445664508055689805, + "(15, 0)": -120750947704259457540904579548327988127, + "(16, 0)": -136415136501850904224107068717733554303, + "(17, 0)": -129806931533375278382276039217105621652, + "(18, 0)": 142608094928441268372771823207290860056, + "(19, 0)": -141486577771584272464753767893525667439, + "(20, 0)": -160766329183686553896170117302168337116, + "(21, 0)": 162812511401152069113611747579037519572, + "(22, 0)": -93889781430314607681956865197258302058, + "(23, 0)": 74324714632072885291941640785876226706, + "(24, 0)": 91688507100163279309073694520012630524, + "(25, 0)": -117163192262590109072324823266179719139, + "(26, 0)": 135246958742832068183923413019729881098, + "(27, 0)": -17889094475226517489193465248086568365, + "(28, 0)": 29953296073632497470732526094259212332, + "(29, 0)": -33206011312087311881958508406984182365, + "(30, 0)": 16667376158424062330686071270667818262, + "(31, 0)": 71425153678471234997279667290533299334, + "(32, 0)": -45419886606476497777964489170043015858, + "(33, 0)": 121475784389002492577933180793492698180, + "(34, 0)": -168009424582891060647440245173157335801, + "(35, 0)": 52669974025177242511654025228302202211, + "(36, 0)": 164331318914827817994390284438567561344, + "(37, 0)": -190455815022318330796924442650925750, + "(38, 0)": 166023865105941643221860477352283636411, + "(39, 0)": 65185880070148925773851769490927134379, + "(40, 0)": -4484306513257173464314244106727122943, + "(41, 0)": -12354075402087986589078306831516251105, + "(42, 0)": -90929140534053587077773168398077169158, + "(43, 0)": -57445027053049420948838146252125514613, + "(44, 0)": 8148166824432725656141720635581211273, + "(45, 0)": -122074305504044419799951297582093595248, + "(46, 0)": -7520704583217469468664607372162992163, + "(47, 0)": 95842691246249844268685579954598578665, + "(48, 0)": 100704033715667301876089114060824894296, + "(49, 0)": 20998202665249411329039933794642285871, + "(50, 0)": -92175851843470109229717302224645691964, + "(51, 0)": -24973980619087702437439617320256999269, + "(52, 0)": -165361775058346956179993115016499496188, + "(53, 0)": -75081152086989456581625155748370037421, + "(54, 0)": -68175871605124794706772158192097870869, + "(55, 0)": -88597300379864704051039380709652347230, + "(56, 0)": -29825691923217368729774026671749021698, + "(57, 0)": -82917893927944415686145756898553558177, + "(58, 0)": 11583819028529864454524570167678973878, + "(59, 0)": -26742583459107932461083953469668910630, + "(60, 0)": -72857447986648542274809121864760339651, + "(61, 0)": -45633354304851321707249208082776675966, + "(62, 0)": -29451416743150127774055895616863266446, + "(63, 0)": -80854363417876698585862240414871492927, + "(64, 0)": -154727939609127908433909346131284234767, + "(65, 0)": 148799548356754163321803910165177251811, + "(66, 0)": -99207519274193030413931982747070872566, + "(67, 0)": 43910152951818445255143456590424872427, + "(68, 0)": 122810283447722230318025059324594280543, + "(69, 0)": -98598356234071962429097631166492206639, + "(70, 0)": 65915394970211107366970224467026154749, + "(71, 0)": 128028620844482718614218394669419472373, + "(72, 0)": 114969094894369367621501340556515347858, + "(73, 0)": 51697196991060085230612295431386666059, + "(74, 0)": 138478458469903598739229117495756304207, + "(75, 0)": -22233601024621851044308696105470932425, + "(76, 0)": -29140058465722570702903871641310026888, + "(77, 0)": 162438615024504838552511149193254810349, + "(78, 0)": -118383959455801467493431257237972218097, + "(79, 0)": 34413769207340695300086566823676008759, + "(80, 0)": -102071996159059123689965136842090943859, + "(81, 0)": 62445310875298889959926993198458707945, + "(82, 0)": -22440252351184257314656966197686062493, + "(83, 0)": 731708610657833595659686100090396293, + "(84, 0)": 49090783469955810741106617152098923273, + "(85, 0)": -17398520902236477611519074120300774684, + "(86, 0)": 12913332269836092183375484636455599480, + "(87, 0)": 23334268618706274252721889463086367733, + "(88, 0)": -104252368433498530294707585749241656295, + "(89, 0)": -108702758524306302668135566635030254117, + "(90, 0)": -53339431151390836383296580708918584381, + "(91, 0)": -152670347647917513717059314742372964134, + "(92, 0)": 59587034733089532881492676876763957361, + "(93, 0)": 141297437117069763327964605472584133221, + "(94, 0)": 130911274870432655593569902216632546083, + "(95, 0)": 6150650725365299599134099946112108027, + "(96, 0)": -74056861157935143762155217379333090732, + "(97, 0)": -152091027609199245063919278193771761853, + "(98, 0)": 132968664034423245042417653565142278802, + "(99, 0)": 46837928086198624013349047416664862958, + "(100, 0)": -11602708595135748080085454507732488613, + "(101, 0)": 4520431311697686872269982317941370397, + "(102, 0)": -2918640660465074446157658013963859351, + "(103, 0)": 4629319922232364160757402836666942282, + "(104, 0)": 69670142292388608885779153548551324524, + "(105, 0)": 97616903437769831985678967545861118218, + "(106, 0)": 72719603009336789395774384431159800818, + "(107, 0)": 113521875705553108259113242528750986208, + "(108, 0)": 36613829242167475118237784108590138031, + "(109, 0)": 142418925495248425888410164548840811622, + "(110, 0)": -48358365952470533010809202713633156649, + "(111, 0)": 7735876904341364410643369986985684592, + "(112, 0)": 40214116667563767257299417218912682212, + "(113, 0)": 158273241725070312455063388132226897112, + "(114, 0)": -35413175365142286651472143899060996083, + "(115, 0)": -119277278269278119902969754430195605514, + "(116, 0)": -11250231342070045502122549703398674541, + "(117, 0)": 77559373391315337447234135114475974532, + "(118, 0)": 3916298800619502172326416614841003162, + "(119, 0)": -65768306924087021115852559071561455874, + "(120, 0)": 82616633061236638203809498016748939956, + "(121, 0)": 10941576588379970316876888325469732362, + "(122, 0)": 15346606904105795845291682116361122037, + "(123, 0)": 140884629485789475879988670688337810327, + "(124, 0)": 68536551903861111551816813297037198058, + "(125, 0)": -34934021839936137008547545294208791278, + "(126, 0)": 11346130392764358442748273202819764632, + "(127, 0)": 62910525030853404097723436946135719573, + "(128, 0)": 83118767267798556149378153396542495132, + "(129, 0)": 87528898141699517101902254360385801830, + "(130, 0)": 128682054034543940445541410068049491901, + "(131, 0)": 123650217423223525499471009808110625983, + "(132, 0)": -143063520655060453136975546435725467908, + "(133, 0)": 139294957321132647096183722269605542429, + "(134, 0)": -138282045532370569087731684440086414819, + "(135, 0)": 25944216847456714744274555912614124926, + "(136, 0)": -79054965604418201482618502154026822113, + "(137, 0)": 44107340640840193871854958013960389580, + "(138, 0)": 89274181879751798682443639898762724586, + "(139, 0)": -34393450124488842678581990974932645833, + "(140, 0)": -138781946372961340169589048909448399878, + "(141, 0)": -81520419876563983267838594698699808035, + "(142, 0)": -7784939806109652978954983532953610928, + "(143, 0)": 35190378795392721889011517532785698781, + "(144, 0)": -132493536956048954822874690248680070863, + "(145, 0)": -44923939450090926314321899284010647738, + "(146, 0)": 19482170874659485951578927047527637235, + "(147, 0)": -42984570149163629472083643955688369946, + "(148, 0)": -155844138210506116194817871618275353200, + "(149, 0)": 13944862144460958588653069294231137254, + "(150, 0)": -155662649167552162208106563270937389278, + "(151, 0)": 6991423744107589753032036358390584590, + "(152, 0)": -3560177884543780076759298637866985266, + "(153, 0)": 118555045811961840528131050966363933096, + "(154, 0)": 95297129741931169419293483035789956195, + "(155, 0)": 41973505753508757638789105503456179831, + "(156, 0)": 10344433762625113149055579331178631661, + "(157, 0)": 130411033183035433788112298791998016346, + "(158, 0)": -31890364319018367096658888008750145489, + "(159, 0)": 55080949842831864349550775148945189534, + "(160, 0)": 108904305163992079302101115820875492517, + "(161, 0)": 2495015403960228398156458493676083737, + "(162, 0)": 164443516174660641429797947284614763590, + "(163, 0)": 147169541595167174569344631208081575802, + "(164, 0)": 13843327337035212970223080896105767025, + "(165, 0)": 3935013969507495140409183364183861472, + "(166, 0)": -46448680459940667170774304690592438454, + "(167, 0)": -82592081130600588180946329099808662639, + "(168, 0)": 115998757284603742484944662891099668062, + "(169, 0)": 129917797975387842351511301470217113251, + "(170, 0)": -56237100225373015313677435624118261605, + "(171, 0)": 159362869660898284299038603474303364649, + "(172, 0)": 87463587384693595896937066898475240823, + "(173, 0)": 158355839609598375722288706179291741121, + "(174, 0)": -158049157462841587594214333280685299967, + "(175, 0)": -76983507963501620785753992696229517133, + "(176, 0)": 170140288193836493156309533157838427692, + "(177, 0)": -73926522566799417363202386532398654861, + "(178, 0)": 127510305875793854421373614977652063335, + "(179, 0)": 117424188438278211029417844151079353662, + "(180, 0)": -117227949208880758052367308704645091466, + "(181, 0)": -82514442721561723621153685439482522203, + "(182, 0)": -23942670528560596842691346333808632770, + "(183, 0)": -78357680274573456828367996056100241292, + "(184, 0)": 87706694738455252743824972773986051180, + "(185, 0)": 3854776483841904143447743560791326995, + "(186, 0)": -56554563222885683246828300315934350356, + "(187, 0)": 79085298855352830493124770252030831193, + "(188, 0)": 100037438762165658817719236242831078144, + "(189, 0)": -114246260161720387625390958931290759749, + "(190, 0)": -93358737930298784459476755041304386381, + "(191, 0)": 154071655564015230062313487255137225086, + "(192, 0)": -91357116575551823421314045505456338132, + "(193, 0)": 53280751630899658517553344765773991584, + "(194, 0)": -3154162992853972081575897891878887202, + "(195, 0)": 145938398760256995088300391899456584223, + "(196, 0)": 56077473124071776705408171088067948164, + "(197, 0)": 4936859974205881277541275701762366470, + "(198, 0)": -126122091208968448579617928489488653656, + "(199, 0)": -33197204137494124346076863016512228178, + "(200, 0)": 54465498319000499339724623694000867645, + "(201, 0)": -22148419700751756996533417726734573305, + "(202, 0)": -131536659643931855557409168719637763296, + "(203, 0)": 99965478927518163185620832484096998665, + "(204, 0)": 48470906900925772930215732596469298649, + "(205, 0)": 47651362234070220969809233200564173880, + "(206, 0)": -127042475422918888309903662835199565319, + "(207, 0)": -125277373697812683871267719331366113336, + "(208, 0)": 59561286352753515963847857503134298237, + "(209, 0)": -154425823336654063413117312538697693465, + "(210, 0)": 34063219564282942895520689862264954869, + "(211, 0)": 37429334655789966713073820490130489755, + "(212, 0)": 9966752019051430476643584736805753062, + "(213, 0)": 43828221374345498655678976439753444435, + "(214, 0)": 112560304170006386732229594036216304045, + "(215, 0)": 73648429832191241865670281955373559362, + "(216, 0)": 38920561300120114629940961765836590629, + "(217, 0)": 148402600144506968491753405019647276052, + "(218, 0)": 133309498509755359996954932952973952495, + "(219, 0)": -123980562395044006692404338716268720062, + "(220, 0)": -4405890177649271480965887313602116086, + "(221, 0)": -153997528408890423963081712257337678888, + "(222, 0)": -54001581161983503230205495523008146245, + "(223, 0)": -3217357454410562101733971290531655653, + "(224, 0)": -169813083690988054407587501753608034251, + "(225, 0)": -137775546514804148741898153040309153256, + "(226, 0)": -23537932903630554654760917694998432032, + "(227, 0)": 130120049321865603598084938652825692632, + "(228, 0)": -97373303432008194606018779190032963123, + "(229, 0)": 105625562150061758042070426796039971663, + "(230, 0)": 73657224468418754968794316661489386133, + "(231, 0)": 157736725862102911267309324335279111390, + "(232, 0)": -110577580047788837161239973385782515684, + "(233, 0)": 164665434857549927434814180664375400893, + "(234, 0)": -84553451301696855978576162491619274663, + "(235, 0)": -117491351387916153746351787791919114302, + "(236, 0)": 38668852880855552180444342620334863442, + "(237, 0)": 48924849353755936079096681984311071459, + "(238, 0)": -19015456032885334288396174348109733318, + "(239, 0)": 10903296022016396544421472641214275027, + "(240, 0)": 44593084406000438373453948017482714466, + "(241, 0)": 54970207154766908019568153245010373476, + "(242, 0)": -100199080644157397571530930003635205275, + "(243, 0)": -107951348782101915334486218171529825233, + "(244, 0)": 143830210779867443522071497774819360059, + "(245, 0)": 54803234442365098406344863526841532479, + "(246, 0)": -91429734085800580097063146159438109838, + "(247, 0)": -83286653856162830647986248809172384245, + "(248, 0)": 151975588746133598385642500750735821157, + "(249, 0)": 75473679197415355765605769961588709608, + "(250, 0)": -149690471061339167212467349151046183332, + "(251, 0)": -123651061551743221597739096586469914952, + "(252, 0)": -153449687277077895338104943951742420267, + "(253, 0)": 463230546477482403872750762249066298, + "(254, 0)": 163163554960798028172172731226139094630, + "(255, 0)": -47285911298162774780655480150577311851, + "(256, 0)": -154025816526573266976834933478590016250, + "(257, 0)": -60282361721199288370739916542528549126, + "(258, 0)": 105642669951938160215702004536904949621, + "(259, 0)": -19143245198780174047074401323228938054, + "(260, 0)": 67593756104254077312924244781555378407, + "(261, 0)": 87547993366627775012749412979726141261, + "(262, 0)": -102814813212078659424206187057207542831, + "(263, 0)": 62384650717014333689997146024100687587, + "(264, 0)": -106846832079507492073910888424708368352, + "(265, 0)": -31787310107997102832294166418449485998, + "(266, 0)": -143531020961127473689048580248557228898, + "(267, 0)": -118321675891786560677652805605100872593, + "(268, 0)": 136515592525978071239219799912136795860, + "(269, 0)": 89538847254843632869932942511643465438, + "(270, 0)": -51924382032188062615739207968786569792, + "(271, 0)": -36031211196833873854802094366546747698, + "(272, 0)": 34620304535158343494086461616193334773, + "(273, 0)": 17962901098031354622743168684030423979, + "(274, 0)": -131197063676527776119742263628154188782, + "(275, 0)": -115969099024054558505191269090302000666, + "(276, 0)": -10364642183606733325543302084706224284, + "(277, 0)": -142836231522261730757149366887316156009, + "(278, 0)": -119618774181141493337575809959179429004, + "(279, 0)": -62056003265295622309252129770994093915, + "(280, 0)": -64414770008422330333201565370664678419, + "(281, 0)": 104379206965048452030991226108193851369, + "(282, 0)": -145556950097822369173310155161856540725, + "(283, 0)": -74086813617569037101529324924537255142, + "(284, 0)": -14100255394311909740571556684443594709, + "(285, 0)": 94792615103943272635050869648104149987, + "(286, 0)": 45431218223086265596849422541029956354, + "(287, 0)": 13697201457212258586297767235133108004, + "(288, 0)": 76124265907422579681398090896829539296, + "(289, 0)": -16644172247492889980034014190661414663, + "(290, 0)": 35231938154495239963414710528199215062, + "(291, 0)": 72143570910757543644245744234762456853, + "(292, 0)": 61138919437014388516568839268041409886, + "(293, 0)": 40367280504880699049700583012434421535, + "(294, 0)": 125739833623265387056039149308619148802, + "(295, 0)": -56786997839720343291992579623519570206, + "(296, 0)": 81516280176624942325874949924559130859, + "(297, 0)": -144200474403173033264948335241062143635, + "(298, 0)": -77790117291699055996838770183387505970, + "(299, 0)": 78103340568633130644414442285789706924, + "(300, 0)": 97882915440270447127303201068550877397, + "(301, 0)": -81595531045628239374110015366935291743, + "(302, 0)": 82010161012549195664716252763309746509, + "(303, 0)": -75676001644243756612340036877405414697, + "(304, 0)": -51182670623604389048008072218532080879, + "(305, 0)": 24581326407076495098480479705602357477, + "(306, 0)": 50753182811024553069069359728944171738, + "(307, 0)": 152418106304005435081276172815447298394, + "(308, 0)": 26707257231791934342418147480300958790, + "(309, 0)": 10678358872142733640042851280916108632, + "(310, 0)": 107581431583253026850144485567332875401, + "(311, 0)": 145753310191129460800640445690439436596, + "(312, 0)": -121055037030525385977640472490852237752, + "(313, 0)": -35962825595069255045779738062932217987, + "(314, 0)": -22693710492006166924705710700758445952, + "(315, 0)": 104331142750363668467353576490950558500, + "(316, 0)": 23577193486118993113877640910275098926, + "(317, 0)": 53319368170327395231216874647939457820, + "(318, 0)": 18828987084488904365871447911404297622, + "(319, 0)": -160127669797288627589718201872119799398, + "(320, 0)": -120188860689491295250933189228301087423, + "(321, 0)": 164047776625927978952051053659515529556, + "(322, 0)": 78168991488301460491065354404134072182, + "(323, 0)": 25035556552493295609695362693198407778, + "(324, 0)": 57500317469205357648931548364782175241, + "(325, 0)": -23308677856561260032999562713235502962, + "(326, 0)": -19041848400428976634873615374439257746, + "(327, 0)": -153336702872912255019698058851836262233, + "(328, 0)": -54295455586836639906094091934508145260, + "(329, 0)": 136451000236112662471206670509799094154, + "(330, 0)": -56753257519687442082591255113927565913, + "(331, 0)": -1131837334144940351171254131644061434, + "(332, 0)": 89821726741350140878433446318465564826, + "(333, 0)": -140968311731472016168782108411367015879, + "(334, 0)": -7392416400925834220088150909073249355, + "(335, 0)": 136008089472190177393422359504881834268, + "(336, 0)": -51212990821480596109880391491804590162, + "(337, 0)": -128464554700236205959480761765737422178, + "(338, 0)": 34154320954271834129983317090765840947, + "(339, 0)": -101250356954165065265172497367003553382, + "(340, 0)": 46521425195160708128681339817156883645, + "(341, 0)": -86286721404271893462892003432471605214, + "(342, 0)": 135740540426028943991172557117818934805, + "(343, 0)": -4092265334849715401800080986538683889, + "(344, 0)": 88783534106479298134119999885952445227, + "(345, 0)": 5042923245991067293344331621529308594, + "(346, 0)": -103998330152237288796170271829115450098, + "(347, 0)": 108993984284643149223549989523912577381, + "(348, 0)": 81097242851651278520797188547605659749, + "(349, 0)": 12337379678978307952652724089907183474, + "(350, 0)": -72984804281989822208834002531309516682, + "(351, 0)": -28606852630258439533956092414464270739, + "(352, 0)": 23949637780734913312069812270606589317, + "(353, 0)": -74211388265783708609705147623538146758, + "(354, 0)": 146457428557154981780408646648484246521, + "(355, 0)": 147588522165683362349005486272690174417, + "(356, 0)": 28874032029682743588906264670067342101, + "(357, 0)": -39116902651644256204174362648835711396, + "(358, 0)": 159837770923903114992971662785434069444, + "(359, 0)": -157787152404351092703531592410125424837, + "(360, 0)": -70966590573753100538049593380543441327, + "(361, 0)": -164382015529930333633966300967426372371, + "(362, 0)": 71918498863055624609896913607975335077, + "(363, 0)": 44575964366391594911744411740460880845, + "(364, 0)": -114439917954668749324653233469747412943, + "(365, 0)": 86712756118407155750695429678920407693, + "(366, 0)": 140176826156488871091127151734755267374, + "(367, 0)": 122925318931879660701493301862524771815, + "(368, 0)": -128741844659527397178557549408872705756, + "(369, 0)": -115235820183142171543691995312479707888, + "(370, 0)": 90989870626903919398549215835701775273, + "(371, 0)": 139872283751442739174545879435029215839, + "(372, 0)": 93595635212480236867235974135875052478, + "(373, 0)": -129400623925247380123462038671389284228, + "(374, 0)": 162816483918974385465181891660392919376, + "(375, 0)": -67937319314310323693324953733463829207, + "(376, 0)": -96363651777098414842661007925816650872, + "(377, 0)": -105426441780407835204423246534576889946, + "(378, 0)": 70715304801157357170569181597220592073, + "(379, 0)": -161775243565504822695773522602150218851, + "(380, 0)": 147613204356088752732099350760845059632, + "(381, 0)": 83673372677534862212787589741256403499, + "(382, 0)": -124860979091821087822453586258767020447, + "(383, 0)": 159668332890174705398231090927482111338, + "(384, 0)": -45783310389421543608669868680202435716, + "(385, 0)": 98803020477991128896097292559018942499, + "(386, 0)": 68160024689505817135741561287783361414, + "(387, 0)": -76335423858314276965123362892368348095, + "(388, 0)": -111020951347880917036005107125574740660, + "(389, 0)": -146264851128120396489091497646868151515, + "(390, 0)": -121337600813419946214974558482865733971, + "(391, 0)": -147313422919856430066938292808770239808, + "(392, 0)": -145166371073204740121027527371509930930, + "(393, 0)": -22039631565433149425078664618992203702, + "(394, 0)": 100715125512904652778064388252911523005, + "(395, 0)": 17898244079303192190730775826201407502, + "(396, 0)": 70012559703972084033093853838471843885, + "(397, 0)": 28238864642897004257127603301623750284, + "(398, 0)": 156024513146977735611288788072551797778, + "(399, 0)": -106545977794964082901854695764876253916, + "(400, 0)": -84334367854789401754960646791430659927, + "(401, 0)": 59009734175720564970617175674522935225, + "(402, 0)": -86657735645610240447341630288897057149, + "(403, 0)": 105210946145253487836936950481689745669, + "(404, 0)": 4145084353282423259530861281937766152, + "(405, 0)": -142449216225297459906336736206560750289, + "(406, 0)": 105352059462505362108359569566208897778, + "(407, 0)": 83347430962590522044662563875998779569, + "(408, 0)": 122347076216717604535250177366741263606, + "(409, 0)": -66822136827426487628144810415911811299, + "(410, 0)": 37615812830323116588912071714715737464, + "(411, 0)": 17105847207686921899259662104594927362, + "(412, 0)": 123810269684944903623846666848914287388, + "(413, 0)": -143883532130719188713449324495357138399, + "(414, 0)": -101847486418503314475603245858375331973, + "(415, 0)": -162112232024000671854741299034816321769, + "(416, 0)": -123309951125113877583831424702841363457, + "(417, 0)": 85206451411182510649975360545263007440, + "(418, 0)": -120103055444915245631941923818572954522, + "(419, 0)": 79277493342078836713517503063051614898, + "(420, 0)": -101990074892167177351440846171691235276, + "(421, 0)": -74473233325212378363672169252262388829, + "(422, 0)": -121872923760386013317156773343202545349, + "(423, 0)": -151672578931151577057390597734941075636, + "(424, 0)": 99530105857545664447711118278618797342, + "(425, 0)": -166472607405331244820480303596822017416, + "(426, 0)": -95659174241523055847523816799161576015, + "(427, 0)": -154251090969815588435128469084402321308, + "(428, 0)": 120455994740420946527727693435332470246, + "(429, 0)": 51005306652663892676297940509799212793, + "(430, 0)": -64716896185903054063119238363694053438, + "(431, 0)": -161205977026566941922481020926234130242, + "(432, 0)": -162096785607513597536744803196473006316, + "(433, 0)": -58568913525357792738135080257083808078, + "(434, 0)": 45639157976912380627078314682402610492, + "(435, 0)": 66257686385497931885086632279613217920, + "(436, 0)": -83882227252506209405440838286831131227, + "(437, 0)": -41530591624937481294965750657294117710, + "(438, 0)": 148712358078412259976734191681755495085, + "(439, 0)": 25389190049975085747996429056982012156, + "(440, 0)": 44058538722053817873573960104738503071, + "(441, 0)": 142348993331366701463154712083948375992, + "(442, 0)": -68028906151077203965416292941674219407, + "(443, 0)": 149156906389894014110810217283359550789, + "(444, 0)": -127733368966072907135911154306419905148, + "(445, 0)": 121211943917804199376910169687356501020, + "(446, 0)": -129005675139018529791056888316906789158, + "(447, 0)": 119712104903079683680334768552851682927, + "(448, 0)": -15218796831780834815015154246906166310, + "(449, 0)": -42204855068852924884233127195657361043, + "(450, 0)": -124066222402940790013621659130361477868, + "(451, 0)": -73944203945728730363931831264551641178, + "(452, 0)": 42154238753875322102854312370047649323, + "(453, 0)": 95583030654745548529946024220549761440, + "(454, 0)": -72700502812065383663012489633914561874, + "(455, 0)": 77006819947738820284504862950245327447, + "(456, 0)": -101326293951626759801742764325529010192, + "(457, 0)": 144476756542143268739586440444352783707, + "(458, 0)": -144167625773396839730025750937802654707, + "(459, 0)": -69545221169587071876458651626495293986, + "(460, 0)": 55936037307326187889742802095321631820, + "(461, 0)": -132880374893584040697828524598590008053, + "(462, 0)": 153293136810645333452114673214948076839, + "(463, 0)": 111544686791633630571070110693172338538, + "(464, 0)": -63749024266164095106698938535243095216, + "(465, 0)": 132954362570926296998872111966522208438, + "(466, 0)": 82442157981388089629420869288528832275, + "(467, 0)": 164884773593542099530363695376294288996, + "(468, 0)": -125473034261473819787587670380754713649, + "(469, 0)": 2853777874649277430074380687889929668, + "(470, 0)": 100507418438070344757277201922633265898, + "(471, 0)": 97022066564559978812307173287923774257, + "(472, 0)": 58149453148938111778100472514262836843, + "(473, 0)": 86468089794429495777153800530388665844, + "(474, 0)": 119625586359473770498136559263996258144, + "(475, 0)": 1654123524005620711633240621870897354, + "(476, 0)": -20640803980052037194593620714387285293, + "(477, 0)": -14432072443761893285908854692904131295, + "(478, 0)": -122529654645955442491279234434449511079, + "(479, 0)": 151305100018667398763409673780134491875, + "(480, 0)": -45270640077095632385685799221844090341, + "(481, 0)": -130363635247196037909310959937324769770, + "(482, 0)": 75298782494944408366683140419756830474, + "(483, 0)": -30691094417371530286402364046473039736, + "(484, 0)": -303197472342312418601001571649279235, + "(485, 0)": -73594925611851112144647543228940239771, + "(486, 0)": -145425517379590652687292707124514874861, + "(487, 0)": 79475149110194904381645277978093031330, + "(488, 0)": 153125046913631628965405620955934800930, + "(489, 0)": -47910064941096930182472905005285759487, + "(490, 0)": 98991666653282161041984824786592612251, + "(491, 0)": -7255310075953484732132405695618492525, + "(492, 0)": -23926217901393978978641463759729911219, + "(493, 0)": -161653483425641021498483866669500353017, + "(494, 0)": 60606511421431421569837717661899084119, + "(495, 0)": 8730931074894274036299198069340762364, + "(496, 0)": -13326365569645896377014265216333811420, + "(497, 0)": -45278374050867452169990733122137805790, + "(498, 0)": -166893148121271485724722820965727119605, + "(499, 0)": 133808639331292792035472477536449645107, + "(500, 0)": -5536289840696116921030855570998217944, + "(501, 0)": 123744448758902568761161403920196092988, + "(502, 0)": -158630117390769578832718637199994043753, + "(503, 0)": 141987434407009207368873399669684702743, + "(504, 0)": 73774685447223421690694406981606119210, + "(505, 0)": -15579919091268510802589484973584810829, + "(506, 0)": -140517499112279734701358292043327602549, + "(507, 0)": -123263040926174367459852069605604559393, + "(508, 0)": -117284209734139894318036355463679368476, + "(509, 0)": 162590545201093967274760619103919466988, + "(510, 0)": -138801325602608239421531200525558693874, + "(511, 0)": 45733793318049424472364115693861681775, + "(512, 0)": 155012826493168171646687493510006464904, + "(513, 0)": -121343673115432599056043317167495446650, + "(514, 0)": 2788182338723026797850580945553809119, + "(515, 0)": 141214313413871441508805074270741897814, + "(516, 0)": 114221690530227220957279004153022381766, + "(517, 0)": 73856342258501319223407852994734691847, + "(518, 0)": 67948631732742394995483872400228265215, + "(519, 0)": -82624108484502074375386133780683607163, + "(520, 0)": -165143413588954693253425479884572319991, + "(521, 0)": -113550102967992171221019828594162685254, + "(522, 0)": 131180504169721467160499835404812832267, + "(523, 0)": 164422713928344282732720615272486593509, + "(524, 0)": 157980231782162104362263482262854493880, + "(525, 0)": -164535718644255252940930394176816849377, + "(526, 0)": -31225353889562600307917509035046364740, + "(527, 0)": -60646062450155763966720217341561984925, + "(528, 0)": -96950233886561722014601761631819412339, + "(529, 0)": 77008287467968324180873563138345644242, + "(530, 0)": -4997872489015742544728343214110400888, + "(531, 0)": -2202395447842202964413412764545989511, + "(532, 0)": 57478095273387845043095340584937890565, + "(533, 0)": -145705677979547998055927000772524724859, + "(534, 0)": -102935487599280398874644321117435564893, + "(535, 0)": 143649118778497972419661400513032796773, + "(536, 0)": -89038304569148187971461117048569701005, + "(537, 0)": -45029837896366546303331400288433846073, + "(538, 0)": 19032560126825381188580714108204185353, + "(539, 0)": -158508131558511784763966326781035256462, + "(540, 0)": 79120878133609244650517186422561785242, + "(541, 0)": 28603998023399676929749006701914687185, + "(542, 0)": 65757691388701874747859916644177031150, + "(543, 0)": 72691922249338035054974336270463527217, + "(544, 0)": 129010265500650440917003661740929453910, + "(545, 0)": 80256719281541042032246705214229026271, + "(546, 0)": -21061132550283983771367061162716903521, + "(547, 0)": 165805406436167401233005042461863960774, + "(548, 0)": 113599655127609455847395518900059443544, + "(549, 0)": 96964270902560568840423856889839503489, + "(550, 0)": 67662840522325901368205026647015202622, + "(551, 0)": -82022774870782805953455083341039144094, + "(552, 0)": 6100136605794926620684126309229400665, + "(553, 0)": -19626146382403430025145110769973357761, + "(554, 0)": -31038901596104973752277995245982837335, + "(555, 0)": -68367015348640280541186050704771610718, + "(556, 0)": -153667021069877568696955575817329133832, + "(557, 0)": 25209357017366486814765650775941423328, + "(558, 0)": -22859159814672150085005583192324232233, + "(559, 0)": -154136032607225218114405047230107033102, + "(560, 0)": -136574384336658168487190901833680114619, + "(561, 0)": 97119890397376601974169032241142219324, + "(562, 0)": -32343289844758779352268915196590034014, + "(563, 0)": -20458222319791570243016621828066855253, + "(564, 0)": -133530524389386826049460636433339871598, + "(565, 0)": -119730534173208319363388378341940491192, + "(566, 0)": 105745360074349395617850913825333108056, + "(567, 0)": 143838816343355455627228825614672639639, + "(568, 0)": 39190575296848492142221327626836615386, + "(569, 0)": 151296918080753581730296211067053482808, + "(570, 0)": 13270089066315449719353507490280790645, + "(571, 0)": 31546478146451830781104606445062538982, + "(572, 0)": 146403816190039212602024520671673208886, + "(573, 0)": -126194715618922530521814769568566830827, + "(574, 0)": 167876308622698256110531769940126595534, + "(575, 0)": 94537717949551111820987884009544103164, + "(576, 0)": 91132020946486161407842485869010894463, + "(577, 0)": -72699415601509752213384249933024285083, + "(578, 0)": -99549812363285969814182438223590590826, + "(579, 0)": 118955466017239699300950990000531678560, + "(580, 0)": 117473696009552458224728000797204195142, + "(581, 0)": -76768892640254460361540198837990407759, + "(582, 0)": 87829778823771504829131716165459798902, + "(583, 0)": -22767408277705487390875068211575666677, + "(584, 0)": -13840777403524625662425714456440880052, + "(585, 0)": 45103739548172109224192330683418946622, + "(586, 0)": -11291307005673376594972191710244298247, + "(587, 0)": -126231492442188213125148186268746334395, + "(588, 0)": 68766723854900005626103147019901605203, + "(589, 0)": 45161629098996725374816654851978395645, + "(590, 0)": -6857173706993157538074548674497757356, + "(591, 0)": 35353421533912367297849454197606302984, + "(592, 0)": -92523977336804313611454842166729641382, + "(593, 0)": 131435972889417498551185801820349633279, + "(594, 0)": -21025892173220995341221605331665321759, + "(595, 0)": 144730372358736537689125641268452451076, + "(596, 0)": -137724331304562991925054402513720291502, + "(597, 0)": 22324519524932728091794524743235726502, + "(598, 0)": 5328537093951599494657310029110149466, + "(599, 0)": -86930497259051491217338242713545766979, + "(600, 0)": 84467694340792797390573091789190593702, + "(601, 0)": -16900800814740785415458854265568301827, + "(602, 0)": 5791442651499726290963703726616502547, + "(603, 0)": -48401071351382590502178109649304939781, + "(604, 0)": -23701857575231309998690523078654566712, + "(605, 0)": -145197088356451341716357041592425456280, + "(606, 0)": 66987097346590375726315099716473740317, + "(607, 0)": -94451483288855112823833837523352328655, + "(608, 0)": 50494673294611566034896235393668434113, + "(609, 0)": -165531438273063060541655197482568497963, + "(610, 0)": 17180560214429396743834123072593867353, + "(611, 0)": 159183972140405939655672291905397735269, + "(612, 0)": -160656804413792085838978366425461536903, + "(613, 0)": 164251013176477463639059511266381860412, + "(614, 0)": -83225335627861817860525419551557585067, + "(615, 0)": -19134268559772726850776155534826217726, + "(616, 0)": -60153315302721360776605760640129422856, + "(617, 0)": -57214522054655968982872015626060272788, + "(618, 0)": 108585122643791880791055020383595180666, + "(619, 0)": -83974270087391182174023970237274629471, + "(620, 0)": 32743999077308106227015597173108907233, + "(621, 0)": -100939486718806652974397415203146075295, + "(622, 0)": -13971833884713873630606676760556474107, + "(623, 0)": -40987919882744959078630995698268337412, + "(624, 0)": -35298757319149370913077397622857113178, + "(625, 0)": -155703439274062120886010376153661501628, + "(626, 0)": 148602157264699539267679273100694409380, + "(627, 0)": 111994979807550190608384267122051071902, + "(628, 0)": 156028728538593962418291653853610685256, + "(629, 0)": 51368207833085284750671895560128331508, + "(630, 0)": -31297462582699303437737135634617403375, + "(631, 0)": -57808915384350644659990134162740435369, + "(632, 0)": 31068678823447670521280147595841180505, + "(633, 0)": 95974590473834171502664035146148363087, + "(634, 0)": -148880946101213602845527741379666533510, + "(635, 0)": 131401930185021352635347139980699617884, + "(636, 0)": 40547232872518751949568429036005189966, + "(637, 0)": -119579677590813499916846786077880583543, + "(638, 0)": -10244016150543405470652064926144794431, + "(639, 0)": 77426967613672161081366947928178433859, + "(640, 0)": 143102658147448064869093679908392988109, + "(641, 0)": -71706327594465183194506459171629837763, + "(642, 0)": -21716423397118024799717145797402775272, + "(643, 0)": -99209741868007476433386226330690105190, + "(644, 0)": 166074207294401966302052302244661944163, + "(645, 0)": -84190467884682076479412003631639834201, + "(646, 0)": -49936530984840544066332984717248046054, + "(647, 0)": -51953275989860591426928070355838162370, + "(648, 0)": 63962881312804266943070456104242626327, + "(649, 0)": -96285035215793313096631966633245370695, + "(650, 0)": 23826645180902563730067338324161481860, + "(651, 0)": 58978932411225640017162112893868392011, + "(652, 0)": -71577849250468039521288236380347363998, + "(653, 0)": 60321938259422794001050310011263315290, + "(654, 0)": -131669936901890603824655000733955776673, + "(655, 0)": 42195951946018264272277310297653880329, + "(656, 0)": 144485473238994540769119137872723084299, + "(657, 0)": -132371629261507379126170254588494742283, + "(658, 0)": 93200757884002300685759450825166545115, + "(659, 0)": -77788728500282692992075224574179184422, + "(660, 0)": 26749505204634724718992839080547927482, + "(661, 0)": -13868713075972353294339745337584355993, + "(662, 0)": -166668867717213441452574923820250362216, + "(663, 0)": -39127764512828531053998634157677887037, + "(664, 0)": -133435498480252534790505428389353161575, + "(665, 0)": -94944953503601592171485227106306236161, + "(666, 0)": 55288497875085384525875163645852869844, + "(667, 0)": -38830308602089096566090097705394714839, + "(668, 0)": -76322017899191845412877444767289685927, + "(669, 0)": 105947277126832268805993216684228748247, + "(670, 0)": -37030002657399322096925549317269744602, + "(671, 0)": 132659347664926314739827551151043811009, + "(672, 0)": -54679831772470725110291766428661234881, + "(673, 0)": 130222298159660173323626331984289727744, + "(674, 0)": 55097091941229985592803598275819915773, + "(675, 0)": 50024921436508389311466775228699046300, + "(676, 0)": 100676068273962493196341107658095272442, + "(677, 0)": 130933331548000313765315372958846898868, + "(678, 0)": -158437006647093655076426628214967242768, + "(679, 0)": 46007435466179905696727345447646061462, + "(680, 0)": 136305981407960823232217712630022874374, + "(681, 0)": -32891650771802131352859410925256927024, + "(682, 0)": 145795970194176824700125345614461382921, + "(683, 0)": -4275578558847631492642058679018514377, + "(684, 0)": 129893550787437872108764213500097378980, + "(685, 0)": 118955303969210197027352603571385595158, + "(686, 0)": 57037239495680113336606385657346737112, + "(687, 0)": 111742564297969630477849431546236880502, + "(688, 0)": -123598810642648123944312384480414872765, + "(689, 0)": -139850085397546783562700903572918567310, + "(690, 0)": -34398005297852767184901552699100050044, + "(691, 0)": -160268529451879041511989330340556514009, + "(692, 0)": 80785586365969248658112705106268245323, + "(693, 0)": 49103134866888415205128860717028666416, + "(694, 0)": -66008947819344558298157196783602189769, + "(695, 0)": 15011479118319453290653762496810800582, + "(696, 0)": -155673540532457283417277238822748966119, + "(697, 0)": -161682392651922480082416188086857638442, + "(698, 0)": 61809387368494170748137778121162089320, + "(699, 0)": -18703335330502771176242522171523686141, + "(700, 0)": -122552335828685390992865143020437458526, + "(701, 0)": -25874871944026625019315775896980401307, + "(702, 0)": 106146432128087778383374268591853914812, + "(703, 0)": 72305994668214709118853309324496757870, + "(704, 0)": -111021265813871549993584208237912162086, + "(705, 0)": 68238556303107411543188529250956442104, + "(706, 0)": 102666047598924401042848613784408606843, + "(707, 0)": 55897818979755968071503515446104054285, + "(708, 0)": -143217856666374632317119207581756015360, + "(709, 0)": 103953530344256971001179513180161119423, + "(710, 0)": -146814428639130131783329196976895875115, + "(711, 0)": 93673441771676787573598506959745133040, + "(712, 0)": -115265555614253888203966758960941257741, + "(713, 0)": -20998072980573279808845161416979466232, + "(714, 0)": 138667198815083705231093791878907676766, + "(715, 0)": -41607819040353431368576904339774336438, + "(716, 0)": 76173400008101651138228036525064594838, + "(717, 0)": 109028301988090427366168062932971930483, + "(718, 0)": -106853147266977857713204814600270568359, + "(719, 0)": -52546278626774846937463006374157266957, + "(720, 0)": 8632241966951064656643128575871395701, + "(721, 0)": -89079620473387902575406535770724228291, + "(722, 0)": 128234098635069785605332824273708422896, + "(723, 0)": 108536241107293988535616147574541220894, + "(724, 0)": 98592632828655787099700086079323733428, + "(725, 0)": 25635608898736233445135521622528546488, + "(726, 0)": -18599823324707243765121419854611011146, + "(727, 0)": -57938594383444270286115715690576083619, + "(728, 0)": -20978286824735108572097013169269037299, + "(729, 0)": -113944894118820026439357742199705652003, + "(730, 0)": 24315072076704846777538179375689179708, + "(731, 0)": -90292269861020716144734285833733703258, + "(732, 0)": -87872804077211657732433841358156805445, + "(733, 0)": 160644190927125402977070703471778899303, + "(734, 0)": -159892367963917899422704786918732694378, + "(735, 0)": -5540684682924934921342855154538570314, + "(736, 0)": -112180811026334101200537708154540526244, + "(737, 0)": 96762995469166616353496009042087541011, + "(738, 0)": 103238765458263096343036219191507104055, + "(739, 0)": -39203114899737705062305201092167483672, + "(740, 0)": -5036512610762760851461206577474670940, + "(741, 0)": 38821524602936873651868659038167344177, + "(742, 0)": 68242488652667147985405200861687208296, + "(743, 0)": 152510737150590202270242123382855821551, + "(744, 0)": -126867530967109499383717536445952078474, + "(745, 0)": -41519747054801735345843999274394924328, + "(746, 0)": 30456797800728897068129599513974163736, + "(747, 0)": -92348981405314762394351222186299701575, + "(748, 0)": 101534645648681791329168023848594119892, + "(749, 0)": -39381457209566035107470433058632572470, + "(750, 0)": 80633107223830588774363049937917525478, + "(751, 0)": 43737478285091495873103074390014240729, + "(752, 0)": -154699486355729127993435313183871989636, + "(753, 0)": -131430821669692099183441667624221394152, + "(754, 0)": -69333061342818777499114393910135458233, + "(755, 0)": -36852578295663122481010501434540754710, + "(756, 0)": -13235659769147043515008975863911675019, + "(757, 0)": 42104446815161553616467895402637638060, + "(758, 0)": 141363767270084587810573687995492084754, + "(759, 0)": -88574933780786105079134785378471071906, + "(760, 0)": -87684348253026713385536763014748974057, + "(761, 0)": -76145288268527560022737331527556433592, + "(762, 0)": 90012789292592915911950323951357077618, + "(763, 0)": 74322857034146026188735725944464743288, + "(764, 0)": -30409004869900927665529449393418370568, + "(765, 0)": 137579042156066881156454334663461880673, + "(766, 0)": -166149569734372313918397462972685333300, + "(767, 0)": 33325380128638854568801312047974900363, + "(768, 0)": -116634262388109264681752733314040490297, + "(769, 0)": 163467784115724085423874434993843554121, + "(770, 0)": -132580985646153666900342700895989154729, + "(771, 0)": 132232976803680126888551102635051029923, + "(772, 0)": -52689326438885641606982136772961982257, + "(773, 0)": 34293663999611707558041531898889639970, + "(774, 0)": -53843680606165314452968980161536120610, + "(775, 0)": 27270736485556967887064455185957620815, + "(776, 0)": 151386001984176495978092149232917206269, + "(777, 0)": 70632599181886317676503231951369691567, + "(778, 0)": 46680902890133367728754901122071490040, + "(779, 0)": 96359827198184532168203849766558095761, + "(780, 0)": -146357244590775925262790284255734987651, + "(781, 0)": 154493324337844332293427507676746613836, + "(782, 0)": 14584649639000144304575295228587759594, + "(783, 0)": -24051198074693977099695984633376668109, + "(784, 0)": 8473934315471309472542984630487738468, + "(785, 0)": -88379487366556486295406376218326178330, + "(786, 0)": -48567395901379205796608137532836730240, + "(787, 0)": -46526071043583047658566411670219882328, + "(788, 0)": 115155924416149686490183141970084950576, + "(789, 0)": 169879088961188354578135809405365472244, + "(790, 0)": -3065114487702546919606580902417540780, + "(791, 0)": 3635352685947913029620789810677029274, + "(792, 0)": -139359793676381716912179640155292498970, + "(793, 0)": -94401537852858700692572192876156358832, + "(794, 0)": -133043580123936369451381038244391535404, + "(795, 0)": -17544629266429194637370800774661569725, + "(796, 0)": 146242889995621147565796237098244357720, + "(797, 0)": 30168722440792092658906364869013752208, + "(798, 0)": -157020591677168485093321152949141129467, + "(799, 0)": -123463886358173354985088990863266054196, + "(800, 0)": -164342040070871170843219758668825564832, + "(801, 0)": 84665652562473825281407750393657139172, + "(802, 0)": -36887804221629364251095167124704329424, + "(803, 0)": 15377357570036672370925116431607603943, + "(804, 0)": 40202565660682346245703943344084319774, + "(805, 0)": 56621051031946670914634190090569742566, + "(806, 0)": 68998911230011820913207446444505918539, + "(807, 0)": 86739469792089327477497318640611059270, + "(808, 0)": -56467207209400346144985020924256918332, + "(809, 0)": 114722189346433287354970903565552528015, + "(810, 0)": -121051481704701846247251477085830767582, + "(811, 0)": -65306001863306589775101974977738383503, + "(812, 0)": 54794263982771118272417684598557132432, + "(813, 0)": 154090890439833958786433141181778633034, + "(814, 0)": -117944408818415420571521001176029900598, + "(815, 0)": -147986383035956505841560940215326443248, + "(816, 0)": -24124946180953357651698672325777961797, + "(817, 0)": 113728374602525486854705966972317131552, + "(818, 0)": 17902080736320743990048164838871418256, + "(819, 0)": -106208121433294220440721847495833743253, + "(820, 0)": -138934262372310152055396418590265213729, + "(821, 0)": -96412411531319121733223916513849598124, + "(822, 0)": -82021707724436268684382711201299588702, + "(823, 0)": -125742079489748439613305964247388853097, + "(824, 0)": -152991553409103830340224853172954203080, + "(825, 0)": -79567596077661657539252435713736065669, + "(826, 0)": -70372013400650077766954679498422666252, + "(827, 0)": 75700908037669465688687211939186910363, + "(828, 0)": 87286888731608470640229238768980302282, + "(829, 0)": -99900013255686486241257735511015357572, + "(830, 0)": 137528600713626187984942844404976314477, + "(831, 0)": -36102022323322024105486953133693334007, + "(832, 0)": 64158155999248162252377377905011644794, + "(833, 0)": -66368610668949394842111501146174649688, + "(834, 0)": 62273554819502062542067509873818845702, + "(835, 0)": 9986817014994610177557736333654338559, + "(836, 0)": -135618956105960760367651504517207502080, + "(837, 0)": -67310368645119518081897023017911442214, + "(838, 0)": -60580965546029891669836179143357461217, + "(839, 0)": -16839884982708883667560395672884196178, + "(840, 0)": -131061138354805255383107775132247252127, + "(841, 0)": 149495458749452835788875226563467335706, + "(842, 0)": -2332201251835045209982347355492478844, + "(843, 0)": 34672763202207295511361325387400583582, + "(844, 0)": -63345366439793790434535089199743984119, + "(845, 0)": -19975219682430007045003530563061736041, + "(846, 0)": -19649456692341693149811773496950554468, + "(847, 0)": -54955767326179667115944630717750077922, + "(848, 0)": -36301637677440570041557364670729433151, + "(849, 0)": -23265236572400358349087124865802529621, + "(850, 0)": -11526305800888677506088324455956654384, + "(851, 0)": 102203964667406634182620183815610740181, + "(852, 0)": 89716399093951700551388642466674675239, + "(853, 0)": 129971425025857379323610701145029238295, + "(854, 0)": -110558905263428317534405273065535185977, + "(855, 0)": -43442795916294247964098944178926657741, + "(856, 0)": -82078882609191454887057577502332722530, + "(857, 0)": 31159065848295360384282248392887538787, + "(858, 0)": -21662483055092868885142008727264245907, + "(859, 0)": -166717456214770038476987039939117552782, + "(860, 0)": -166417933394777417985917081316022872177, + "(861, 0)": -131663553485736907064090696352593036794, + "(862, 0)": -132835976782750203556743080848508333856, + "(863, 0)": -9341250422138467138266027822915257770, + "(864, 0)": 90793487633952365236980574748582382495, + "(865, 0)": 28283677778330720570875770140293296033, + "(866, 0)": 154596484485577077276546511710292645690, + "(867, 0)": -112727520513731396697448453411897463222, + "(868, 0)": -109816986315935495362175788788554892590, + "(869, 0)": 158186962789226913388921194291965597505, + "(870, 0)": -89021912426681080409036565305657585231, + "(871, 0)": 52754565653812980175518796024307172743, + "(872, 0)": 60309709376160427682115060231512421942, + "(873, 0)": -46862502241845243910669480508749092925, + "(874, 0)": 28807102067685643105321481810413101014, + "(875, 0)": -96212324929105928122296957314028858736, + "(876, 0)": 3670931811459528, + "(877, 0)": -74927100548879103577168617728673172161, + "(878, 0)": -321304393872, + "(880, 0)": 8168202 + } + ] +} diff --git a/test/data/ec/__init__.py b/test/data/ec/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/data/ec/__init__.py diff --git a/test/data/curve.json b/test/data/ec/curve.json index c965c90..c965c90 100644 --- a/test/data/curve.json +++ b/test/data/ec/curve.json diff --git a/test/data/curves.json b/test/data/ec/curves.json index 978ab36..978ab36 100644 --- a/test/data/curves.json +++ b/test/data/ec/curves.json diff --git a/test/data/ec/ecgen_secp128r1.json b/test/data/ec/ecgen_secp128r1.json new file mode 100644 index 0000000..a1c4796 --- /dev/null +++ b/test/data/ec/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/ec/ectester_secp128r1.csv b/test/data/ec/ectester_secp128r1.csv new file mode 100644 index 0000000..f86a00d --- /dev/null +++ b/test/data/ec/ectester_secp128r1.csv @@ -0,0 +1 @@ +0xfffffffdffffffffffffffffffffffff,0xfffffffdfffffffffffffffffffffffc,0xe87579c11079f43dd824993c2cee5ed3,0x161ff7528b899b2d0c28607ca52c5b86,0xcf5ac8395bafeb13c02da292dded7a83,0xfffffffe0000000075a30d1b9038a115,0x1 diff --git a/test/data/sca/__init__.py b/test/data/sca/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/data/sca/__init__.py diff --git a/test/data/chipwhisperer_keylist.npy b/test/data/sca/chipwhisperer_keylist.npy Binary files differindex c709dc7..c709dc7 100644 --- a/test/data/chipwhisperer_keylist.npy +++ b/test/data/sca/chipwhisperer_keylist.npy diff --git a/test/data/chipwhisperer_knownkey.npy b/test/data/sca/chipwhisperer_knownkey.npy Binary files differindex 82c97cd..82c97cd 100644 --- a/test/data/chipwhisperer_knownkey.npy +++ b/test/data/sca/chipwhisperer_knownkey.npy diff --git a/test/data/chipwhisperer_settings.cwset b/test/data/sca/chipwhisperer_settings.cwset index e6d0ad4..e6d0ad4 100644 --- a/test/data/chipwhisperer_settings.cwset +++ b/test/data/sca/chipwhisperer_settings.cwset diff --git a/test/data/chipwhisperer_textin.npy b/test/data/sca/chipwhisperer_textin.npy Binary files differindex 22b93e1..22b93e1 100644 --- a/test/data/chipwhisperer_textin.npy +++ b/test/data/sca/chipwhisperer_textin.npy diff --git a/test/data/chipwhisperer_textout.npy b/test/data/sca/chipwhisperer_textout.npy Binary files differindex 0ac8831..0ac8831 100644 --- a/test/data/chipwhisperer_textout.npy +++ b/test/data/sca/chipwhisperer_textout.npy diff --git a/test/data/chipwhisperer_traces.npy b/test/data/sca/chipwhisperer_traces.npy Binary files differindex df72f86..df72f86 100644 --- a/test/data/chipwhisperer_traces.npy +++ b/test/data/sca/chipwhisperer_traces.npy diff --git a/test/data/config_chipwhisperer_.cfg b/test/data/sca/config_chipwhisperer_.cfg index dcc3129..dcc3129 100644 --- a/test/data/config_chipwhisperer_.cfg +++ b/test/data/sca/config_chipwhisperer_.cfg diff --git a/test/data/example.trs b/test/data/sca/example.trs Binary files differindex a432e69..a432e69 100644 --- a/test/data/example.trs +++ b/test/data/sca/example.trs diff --git a/test/data/target.py b/test/data/sca/target.py index 72fc463..3ed71d1 100755 --- a/test/data/target.py +++ b/test/data/sca/target.py @@ -4,7 +4,10 @@ from sys import stdout if __name__ == "__main__": while True: - line = input() + try: + line = input() + except EOFError: + break char = line[0] content = line[1:] if char == "d": diff --git a/test/data/test.h5 b/test/data/sca/test.h5 Binary files differindex f51b5fd..f51b5fd 100644 --- a/test/data/test.h5 +++ b/test/data/sca/test.h5 diff --git a/test/data/test.pickle b/test/data/sca/test.pickle Binary files differindex 43f7f07..43f7f07 100644 --- a/test/data/test.pickle +++ b/test/data/sca/test.pickle diff --git a/test/ec/conftest.py b/test/ec/conftest.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/ec/conftest.py diff --git a/test/ec/perf_divpoly.py b/test/ec/perf_divpoly.py new file mode 100755 index 0000000..2937af1 --- /dev/null +++ b/test/ec/perf_divpoly.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python +import click + +from pyecsca.ec.divpoly import mult_by_n +from pyecsca.ec.params import get_params +from datetime import datetime + + +@click.command() +@click.option("-n", type=click.INT, default=21) +def main(n): + p256 = get_params("secg", "secp256r1", "projective") + + ns = [] + durs = [] + mems = [] + for i in range(2, n): + start = datetime.now() + mx, my = mult_by_n(p256.curve, i) + end = datetime.now() + duration = (end - start).total_seconds() + memory = (mx[0].degree() + mx[1].degree() + my[0].degree() + my[1].degree()) * 32 + ns.append(i) + durs.append((end - start).total_seconds()) + mems.append(memory) + print(i, duration, memory, sep=",") + + +if __name__ == "__main__": + main() diff --git a/test/ec/perf_mult.py b/test/ec/perf_mult.py index 51fb5a9..521f5e7 100755 --- a/test/ec/perf_mult.py +++ b/test/ec/perf_mult.py @@ -1,6 +1,9 @@ #!/usr/bin/env python +from typing import cast + import click +from pyecsca.ec.formula import AdditionFormula, DoublingFormula from pyecsca.ec.mod import has_gmp from pyecsca.ec.mult import LTRMultiplier from pyecsca.ec.params import get_params @@ -29,8 +32,8 @@ def main(profiler, mod, operations, directory): cfg.ec.mod_implementation = mod p256 = get_params("secg", "secp256r1", "projective") coords = p256.curve.coordinate_model - add = coords.formulas["add-2016-rcb"] - dbl = coords.formulas["dbl-2016-rcb"] + add = cast(AdditionFormula, coords.formulas["add-2016-rcb"]) + dbl = cast(DoublingFormula, coords.formulas["dbl-2016-rcb"]) mult = LTRMultiplier(add, dbl) click.echo( f"Profiling {operations} {p256.curve.prime.bit_length()}-bit scalar multiplication executions..." diff --git a/test/ec/test_configuration.py b/test/ec/test_configuration.py index d2ab3f5..17fb6f7 100644 --- a/test/ec/test_configuration.py +++ b/test/ec/test_configuration.py @@ -1,4 +1,4 @@ -from unittest import TestCase +import pytest from pyecsca.ec.configuration import ( all_configurations, @@ -11,89 +11,46 @@ from pyecsca.ec.configuration import ( ) from pyecsca.ec.model import ShortWeierstrassModel from pyecsca.ec.mult import LTRMultiplier -from .utils import slow -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, } - @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) +@pytest.mark.slow +def test_all(): + j = 0 + for _ in all_configurations(model=ShortWeierstrassModel()): + j += 1 - 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_weierstrass_projective(base_independents): + model = ShortWeierstrassModel() + coords = model.coordinates["projective"] + configs = list(all_configurations(model=model, coords=coords, **base_independents)) + assert 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_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_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(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_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([]) - 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) + assert tree.get_by_index([0]) == ("a", a) + assert tree.get_by_index([0, 0]) == ("d", d) -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_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 - 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) +@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_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_null(mult): + with local() as ctx: + mult.multiply(59) + assert ctx is None - 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_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 345d3de..cd0639a 100644 --- a/test/ec/test_curve.py +++ b/test/ec/test_curve.py @@ -1,177 +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.params import get_params +from pyecsca.ec.error import UnsatisfiedAssumptionError 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(secp128r1): + with pytest.raises(ValueError): + EllipticCurve( + MontgomeryModel(), + secp128r1.curve.coordinate_model, + 1, + InfinityPoint(secp128r1.curve.coordinate_model), + parameters={}, + ) - def test_init(self): - with self.assertRaises(ValueError): - EllipticCurve( - MontgomeryModel(), - self.secp128r1.curve.coordinate_model, - 1, - InfinityPoint(self.secp128r1.curve.coordinate_model), - parameters={}, - ) + with pytest.raises(ValueError): + EllipticCurve( + secp128r1.curve.model, + secp128r1.curve.coordinate_model, + 15, + InfinityPoint(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={"c": 0}, - ) + 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)}, + ) - 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_is_neutral(self): - self.assertTrue( - self.secp128r1.curve.is_neutral( - InfinityPoint(self.secp128r1.curve.coordinate_model) - ) - ) +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_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), - ) - 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)) +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 - 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 - ) - 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_is_neutral(secp128r1): + assert secp128r1.curve.is_neutral( + InfinityPoint(secp128r1.curve.coordinate_model) + ) - 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, - ) - 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_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_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_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 - 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)) + 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_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_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_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) +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) - 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")) +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 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 new file mode 100644 index 0000000..07eed76 --- /dev/null +++ b/test/ec/test_divpoly.py @@ -0,0 +1,112 @@ +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 + + +def test_ainvs(secp128r1): + ainvs = a_invariants(secp128r1.curve) + assert ainvs == (0, 0, 0, 340282366762482138434845932244680310780, 308990863222245658030922601041482374867) + + +def test_binvs(secp128r1): + binvs = b_invariants(secp128r1.curve) + assert binvs == (0, 340282366762482138434845932244680310777, 215116352601536216819152607431888567119, + 340282366762482138434845932244680310774) + + +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(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(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 + + +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" - ] +@pytest.fixture() +def add(secp128r1): + return secp128r1.curve.coordinate_model.formulas["add-2007-bl"] - 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(), - ) - def test_indices(self): - self.assertEqual(self.add.input_index, 1) - self.assertEqual(self.add.output_index, 3) +@pytest.fixture() +def dbl(secp128r1): + return secp128r1.curve.coordinate_model.formulas["dbl-2007-bl"] - 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) +@pytest.fixture() +def mdbl(secp128r1): + return secp128r1.curve.coordinate_model.formulas["mdbl-2007-bl"] - 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 - ) - self.assertIsNotNone(res) +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_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} +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 ) - 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)) + with TemporaryConfig() as cfg: + cfg.ec.unsatisfied_formula_assumption_action = "ignore" + pt = mdbl( + secp128r1.curve.prime, other, **secp128r1.curve.parameters + ) + 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)) +@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 + - @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) +@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. +# 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..55522b0 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,147 @@ 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_gcd(): + assert gcd(15, 20) == 5 + assert extgcd(15, 0) == (1, 0, 15) + assert 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_jacobi(): + assert jacobi(5, 1153486465415345646578465454655646543248656451) == 1 + assert jacobi(564786456646845, 46874698564153465453246546545456849797895547657) == -1 + assert jacobi(564786456646845, 46874698564153465453246546545456849797895) == 0 - 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" + +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() - getconfig().ec.no_inverse_action = "error" + 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(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_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() + getconfig().ec.non_residue_action = "warning" + with warnings.catch_warnings(record=True) as w: 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" + 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(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_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(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_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_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_wrong_mod(): + a = Mod(5, 7) + b = Mod(4, 11) + with pytest.raises(ValueError): + a + b - def test_undefined(self): - u = Undefined() - for k, meth in u.__class__.__dict__.items(): - if k in ( + +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 +174,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 + ): + 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_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)) +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..c7edd03 100644 --- a/test/ec/test_mult.py +++ b/test/ec/test_mult.py @@ -1,8 +1,6 @@ -from unittest import TestCase -from parameterized import parameterized +import pytest -from pyecsca.ec.params import get_params from pyecsca.ec.mult import ( LTRMultiplier, RTLMultiplier, @@ -13,320 +11,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 +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 + ) + 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) - @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), - ] + +@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 + ) + b = do_basic_test( + LTRMultiplier, secp128r1, secp128r1.generator, add, dbl, scale, always=True + ) + c = do_basic_test( + LTRMultiplier, secp128r1, secp128r1.generator, add, dbl, scale, complete=False ) - def test_rtl(self, name, add, dbl, scale): - self.do_basic_test(RTLMultiplier, 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) + + +@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) - @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 test_ladder(curve25519): + a = do_basic_test( + LadderMultiplier, + curve25519, + curve25519.generator, + "ladd-1987-m", + "dbl-1987-m", + "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) + b = do_basic_test( + LadderMultiplier, + curve25519, + curve25519.generator, + "ladd-1987-m", + "dbl-1987-m", + "scale", + complete=False, + ) + assert_pt_equality(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), - ] +@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_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), - ] +@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_simple_ladder(self, name, add, dbl, scale): - self.do_basic_test( - SimpleLadderMultiplier, self.secp128r1, self.base, add, dbl, 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) + - @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,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 ) - 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), - ] + +@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 ) - def test_binary_naf(self, name, add, dbl, neg, scale): - self.do_basic_test( - BinaryNAFMultiplier, self.secp128r1, self.base, add, dbl, scale, neg - ) + mult.init(secp128r1, secp128r1.generator) + res_precompute = mult.multiply(157 * 789) + assert_pt_equality(res_precompute, res, scale) + - @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,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"], ) - 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)) + with pytest.raises(ValueError): + rtl.multiply(1) + rtl.init(secp128r1, secp128r1.generator) + res_rtl = rtl.multiply(num) + assert res_ltr == res_rtl - 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) + 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 - @parameterized.expand( - cartesian( - [ - ("10", 10), - ("2355498743", 2355498743), - ( - "325385790209017329644351321912443757746", - 325385790209017329644351321912443757746, - ), - ], - [("add-1998-cmo", "dbl-1998-cmo"), ("add-2016-rcb", "dbl-2016-rcb")], - ) + 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"], ) - 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) + with pytest.raises(ValueError): + bnaf.multiply(1) + bnaf.init(secp128r1, secp128r1.generator) + res_bnaf = bnaf.multiply(num) + assert res_bnaf == res_ltr - 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) + 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 - 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) + 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 - 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) + 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 - 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(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) - def test_init_fail(self): - mult = DifferentialLadderMultiplier( - self.coords25519.formulas["dadd-1987-m"], - self.coords25519.formulas["dbl-1987-m"], - self.coords25519.formulas["scale"], + 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 833272d..ceaef48 100644 --- a/test/ec/test_params.py +++ b/test/ec/test_params.py @@ -1,122 +1,144 @@ -from unittest import TestCase +from importlib_resources import files, as_file -from parameterized import parameterized +import pytest +import test.data.ec from pyecsca.ec.mod import Mod 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 -class DomainParameterTests(TestCase): - def setUp(self): - self.secp128r1 = get_params("secg", "secp128r1", "projective") - self.curve25519 = get_params("other", "Curve25519", "xz") +def test_eq(secp128r1, curve25519): + assert secp128r1 == secp128r1 + assert secp128r1 != curve25519 + assert secp128r1 is not None - 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)") +def test_str(secp128r1): + assert str(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) - 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) +@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 + - def test_load_params(self): - params = load_params("test/data/curve.json", "projective") +@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 - def test_load_category(self): - category = load_category("test/data/curves.json", "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_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_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_extension(): + with pytest.raises(ValueError): + get_params("other", "Fp254n2BNa", "something") - 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(): + aff = get_params("secg", "secp128r1", "affine") + assert isinstance(aff.curve.coordinate_model, AffineCoordinateModel) - 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) +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) - self.assertIsNotNone(params) - res = params.curve.affine_double(generator.to_affine()) - self.assertIsNotNone(res) + 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..4a5a6f6 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() +@pytest.fixture() +def coords(secp128r1): + return secp128r1.curve.coordinate_model - 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) +@pytest.fixture() +def affine_model(): + return AffineCoordinateModel(ShortWeierstrassModel()) - 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)) +def test_construction(coords): + with pytest.raises(ValueError): + Point(coords) - 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_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() - 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) + 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 - 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)) + affine = InfinityPoint(coords).to_affine() + assert isinstance(affine, InfinityPoint) - 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_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) - 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") + 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) - 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) + 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) # type: ignore + 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..04321f2 100644 --- a/test/ec/test_signature.py +++ b/test/ec/test_signature.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.mod import Mod from pyecsca.ec.mult import LTRMultiplier from pyecsca.ec.signature import ( @@ -17,81 +13,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) +@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 - @parameterized.expand( - [ - ("SHA1", ECDSA_SHA1), - ("SHA224", ECDSA_SHA224), - ("SHA256", ECDSA_SHA256), - ("SHA384", ECDSA_SHA384), - ("SHA512", ECDSA_SHA512), - ] - ) - 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)) +@pytest.fixture() +def msg(): + return 0xCAFEBABE.to_bytes(4, byteorder="big") - 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) +@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 + ) + digest = signer.hash_algo(msg).digest() + sig = none.sign_hash(digest) + assert none.verify_hash(sig, digest) - 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), - ] +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) + 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(self): - sig = SignatureResult(0xAAAAA, 0xBBBBB) - self.assertEqual(sig, SignatureResult.from_DER(sig.to_DER())) - self.assertNotEqual(sig, "abc") +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 67a9cc0..1f32033 100644 --- a/test/ec/utils.py +++ b/test/ec/utils.py @@ -2,11 +2,6 @@ from itertools import product from functools import reduce -def slow(func): - func.slow = 1 - return func - - def cartesian(*items): for cart in product(*items): yield reduce(lambda x, y: x + y, cart) diff --git a/test/sca/conftest.py b/test/sca/conftest.py new file mode 100644 index 0000000..a7a21d6 --- /dev/null +++ b/test/sca/conftest.py @@ -0,0 +1,43 @@ +from typing import Dict + +import pytest +from importlib_resources import files, as_file + +import matplotlib.pyplot as plt + +from pyecsca.sca import Trace + +cases: Dict[str, int] = {} + + +@pytest.fixture() +def plot_name(request): + def namer(): + test_name = f"{request.module.__name__}.{request.node.name}" + case_id = cases.setdefault(test_name, 0) + 1 + cases[test_name] = case_id + return test_name + str(case_id) + return namer + + +@pytest.fixture() +def plot_path(plot_name): + def namer(): + with as_file(files("test").joinpath("plots", plot_name())) as fname: + return fname + return namer + + +@pytest.fixture() +def plot(plot_path): + def plotter(*traces: Trace, **kwtraces: Trace): + fig = plt.figure() + ax = fig.add_subplot(111) + for i, trace in enumerate(traces): + ax.plot(trace.samples, label=str(i)) + for name, trace in kwtraces.items(): + ax.plot(trace.samples, label=name) + ax.legend(loc="best") + fname = plot_path() + plt.savefig(fname.parent / (fname.name + ".png")) + return plotter diff --git a/test/sca/perf_combine.py b/test/sca/perf_combine.py index 9b934d7..b76acb1 100644 --- a/test/sca/perf_combine.py +++ b/test/sca/perf_combine.py @@ -1,7 +1,9 @@ #!/usr/bin/env python import click +from importlib_resources import files, as_file from test.utils import Profiler +import test.data.sca from pyecsca.sca import ( InspectorTraceSet, average, @@ -24,25 +26,26 @@ from pyecsca.sca import ( envvar="DIR", ) def main(profiler, operations, directory): - traces = InspectorTraceSet.read("test/data/example.trs") - with Profiler(profiler, directory, f"combine_average_example_{operations}"): - for _ in range(operations): - average(*traces) - with Profiler(profiler, directory, f"combine_condavg_example_{operations}"): - for _ in range(operations): - conditional_average(*traces, condition=lambda trace: trace[0] > 0) - with Profiler(profiler, directory, f"combine_variance_example_{operations}"): - for _ in range(operations): - variance(*traces) - with Profiler(profiler, directory, f"combine_stddev_example_{operations}"): - for _ in range(operations): - standard_deviation(*traces) - with Profiler(profiler, directory, f"combine_add_example_{operations}"): - for _ in range(operations): - add(*traces) - with Profiler(profiler, directory, f"combine_subtract_example_{operations}"): - for _ in range(operations): - subtract(traces[0], traces[1]) + with as_file(files(test.data.sca).joinpath("example.trs")) as path: + traces = InspectorTraceSet.read(path) + with Profiler(profiler, directory, f"combine_average_example_{operations}"): + for _ in range(operations): + average(*traces) + with Profiler(profiler, directory, f"combine_condavg_example_{operations}"): + for _ in range(operations): + conditional_average(*traces, condition=lambda trace: trace[0] > 0) + with Profiler(profiler, directory, f"combine_variance_example_{operations}"): + for _ in range(operations): + variance(*traces) + with Profiler(profiler, directory, f"combine_stddev_example_{operations}"): + for _ in range(operations): + standard_deviation(*traces) + with Profiler(profiler, directory, f"combine_add_example_{operations}"): + for _ in range(operations): + add(*traces) + with Profiler(profiler, directory, f"combine_subtract_example_{operations}"): + for _ in range(operations): + subtract(traces[0], traces[1]) if __name__ == "__main__": diff --git a/test/sca/test_align.py b/test/sca/test_align.py index 1db71b3..ad45d86 100644 --- a/test/sca/test_align.py +++ b/test/sca/test_align.py @@ -1,4 +1,6 @@ import numpy as np +import pytest +from importlib_resources import files, as_file from pyecsca.sca import ( align_correlation, align_peaks, @@ -8,141 +10,140 @@ from pyecsca.sca import ( Trace, InspectorTraceSet, ) -from .utils import Plottable, slow +import test.data.sca -class AlignTests(Plottable): - def test_align(self): - first_arr = np.array( - [10, 64, 120, 64, 10, 10, 10, 10, 10], dtype=np.dtype("i1") - ) - second_arr = np.array([10, 10, 10, 10, 50, 80, 50, 20], dtype=np.dtype("i1")) - third_arr = np.array([70, 30, 42, 35, 28, 21, 15, 10, 5], dtype=np.dtype("i1")) - a = Trace(first_arr) - b = Trace(second_arr) - c = Trace(third_arr) - result, offsets = align_correlation( - a, - b, - c, - reference_offset=1, - reference_length=3, - max_offset=4, - min_correlation=0.65, - ) - self.assertIsNotNone(result) - self.assertEqual(len(result), 2) - np.testing.assert_equal(result[0].samples, first_arr) - np.testing.assert_equal( - result[1].samples, - np.array([10, 50, 80, 50, 20, 0, 0, 0], dtype=np.dtype("i1")), - ) - self.assertEqual(len(offsets), 2) - self.assertEqual(offsets[0], 0) - self.assertEqual(offsets[1], 3) +def test_align(): + first_arr = np.array( + [10, 64, 120, 64, 10, 10, 10, 10, 10], dtype=np.dtype("i1") + ) + second_arr = np.array([10, 10, 10, 10, 50, 80, 50, 20], dtype=np.dtype("i1")) + third_arr = np.array([70, 30, 42, 35, 28, 21, 15, 10, 5], dtype=np.dtype("i1")) + a = Trace(first_arr) + b = Trace(second_arr) + c = Trace(third_arr) + result, offsets = align_correlation( + a, + b, + c, + reference_offset=1, + reference_length=3, + max_offset=4, + min_correlation=0.65, + ) + assert result is not None + assert len(result) == 2 + np.testing.assert_equal(result[0].samples, first_arr) + np.testing.assert_equal( + result[1].samples, + np.array([10, 50, 80, 50, 20, 0, 0, 0], dtype=np.dtype("i1")), + ) + assert len(offsets) == 2 + assert offsets[0] == 0 + assert offsets[1] == 3 + - @slow - def test_large_align(self): - example = InspectorTraceSet.read("test/data/example.trs") +@pytest.mark.slow +def test_large_align(): + with as_file(files(test.data.sca).joinpath("example.trs")) as path: + example = InspectorTraceSet.read(path) result, _ = align_correlation( *example, reference_offset=100000, reference_length=20000, max_offset=15000 ) - self.assertIsNotNone(result) + assert result is not None - @slow - def test_large_dtw_align(self): - example = InspectorTraceSet.read("test/data/example.trs") + +@pytest.mark.slow +def test_large_dtw_align(): + with as_file(files(test.data.sca).joinpath("example.trs")) as path: + example = InspectorTraceSet.read(path) result = align_dtw(*example[:5]) - self.assertIsNotNone(result) + assert result is not None - def test_peak_align(self): - first_arr = np.array( - [10, 64, 14, 120, 15, 30, 10, 15, 20, 15, 15, 10, 10], dtype=np.dtype("i1") - ) - second_arr = np.array( - [10, 10, 10, 10, 90, 40, 50, 20, 10, 17, 16, 10], dtype=np.dtype("i1") - ) - a = Trace(first_arr) - b = Trace(second_arr) - result, _ = align_peaks( - a, b, reference_offset=2, reference_length=5, max_offset=3 - ) - self.assertEqual(np.argmax(result[0].samples), np.argmax(result[1].samples)) - def test_sad_align(self): - first_arr = np.array( - [10, 64, 14, 120, 15, 30, 10, 15, 20, 15, 15, 10, 10], dtype=np.dtype("i1") - ) - second_arr = np.array( - [10, 10, 90, 40, 50, 20, 10, 17, 16, 10, 10], dtype=np.dtype("i1") - ) - a = Trace(first_arr) - b = Trace(second_arr) - result, _ = align_sad( - a, b, reference_offset=2, reference_length=5, max_offset=3 - ) - self.assertEqual(len(result), 2) +def test_peak_align(): + first_arr = np.array( + [10, 64, 14, 120, 15, 30, 10, 15, 20, 15, 15, 10, 10], dtype=np.dtype("i1") + ) + second_arr = np.array( + [10, 10, 10, 10, 90, 40, 50, 20, 10, 17, 16, 10], dtype=np.dtype("i1") + ) + a = Trace(first_arr) + b = Trace(second_arr) + result, _ = align_peaks( + a, b, reference_offset=2, reference_length=5, max_offset=3 + ) + assert np.argmax(result[0].samples) == np.argmax(result[1].samples) - def test_dtw_align_scale(self): - first_arr = np.array( - [10, 64, 14, 120, 15, 30, 10, 15, 20, 15, 15, 10, 10, 8, 10, 12, 10, 13, 9], - dtype=np.dtype("f2"), - ) - second_arr = np.array( - [10, 10, 60, 40, 90, 20, 10, 17, 16, 10, 10, 10, 10, 10, 17, 12, 10], - dtype=np.dtype("f2"), - ) - third_arr = np.array( - [10, 30, 20, 21, 15, 8, 10, 37, 21, 77, 20, 28, 25, 10, 9, 10, 15, 9, 10], - dtype=np.dtype("f2"), - ) - a = Trace(first_arr) - b = Trace(second_arr) - c = Trace(third_arr) - result = align_dtw_scale(a, b, c) - self.assertEqual(np.argmax(result[0].samples), np.argmax(result[1].samples)) - self.assertEqual(np.argmax(result[1].samples), np.argmax(result[2].samples)) - self.plot(*result) +def test_sad_align(): + first_arr = np.array( + [10, 64, 14, 120, 15, 30, 10, 15, 20, 15, 15, 10, 10], dtype=np.dtype("i1") + ) + second_arr = np.array( + [10, 10, 90, 40, 50, 20, 10, 17, 16, 10, 10], dtype=np.dtype("i1") + ) + a = Trace(first_arr) + b = Trace(second_arr) + result, _ = align_sad( + a, b, reference_offset=2, reference_length=5, max_offset=3 + ) + assert len(result) == 2 - result_other = align_dtw_scale(a, b, c, fast=False) - self.assertEqual( - np.argmax(result_other[0].samples), np.argmax(result_other[1].samples) - ) - self.assertEqual( - np.argmax(result_other[1].samples), np.argmax(result_other[2].samples) - ) - self.plot(*result_other) +def test_dtw_align(plot): + first_arr = np.array( + [10, 64, 14, 120, 15, 30, 10, 15, 20, 15, 15, 10, 10, 8, 10, 12, 10, 13, 9], + dtype=np.dtype("i1"), + ) + second_arr = np.array( + [10, 10, 60, 40, 90, 20, 10, 17, 16, 10, 10, 10, 10, 10, 17, 12, 10], + dtype=np.dtype("i1"), + ) + third_arr = np.array( + [10, 30, 20, 21, 15, 8, 10, 47, 21, 77, 20, 28, 25, 10, 9, 10, 15, 9, 10], + dtype=np.dtype("i1"), + ) + a = Trace(first_arr) + b = Trace(second_arr) + c = Trace(third_arr) + result = align_dtw(a, b, c) - def test_dtw_align(self): - first_arr = np.array( - [10, 64, 14, 120, 15, 30, 10, 15, 20, 15, 15, 10, 10, 8, 10, 12, 10, 13, 9], - dtype=np.dtype("i1"), - ) - second_arr = np.array( - [10, 10, 60, 40, 90, 20, 10, 17, 16, 10, 10, 10, 10, 10, 17, 12, 10], - dtype=np.dtype("i1"), - ) - third_arr = np.array( - [10, 30, 20, 21, 15, 8, 10, 47, 21, 77, 20, 28, 25, 10, 9, 10, 15, 9, 10], - dtype=np.dtype("i1"), - ) - a = Trace(first_arr) - b = Trace(second_arr) - c = Trace(third_arr) - result = align_dtw(a, b, c) + assert np.argmax(result[0].samples) == np.argmax(result[1].samples) + assert np.argmax(result[1].samples) == np.argmax(result[2].samples) + plot(*result) - self.assertEqual(np.argmax(result[0].samples), np.argmax(result[1].samples)) - self.assertEqual(np.argmax(result[1].samples), np.argmax(result[2].samples)) - self.plot(*result) + result_other = align_dtw(a, b, c, fast=False) - result_other = align_dtw(a, b, c, fast=False) + assert np.argmax(result_other[0].samples) == np.argmax(result_other[1].samples) + assert np.argmax(result_other[1].samples) == np.argmax(result_other[2].samples) + plot(*result_other) - self.assertEqual( - np.argmax(result_other[0].samples), np.argmax(result_other[1].samples) - ) - self.assertEqual( - np.argmax(result_other[1].samples), np.argmax(result_other[2].samples) - ) - self.plot(*result_other) + +def test_dtw_align_scale(plot): + first_arr = np.array( + [10, 64, 14, 120, 15, 30, 10, 15, 20, 15, 15, 10, 10, 8, 10, 12, 10, 13, 9], + dtype=np.dtype("f2"), + ) + second_arr = np.array( + [10, 10, 60, 40, 90, 20, 10, 17, 16, 10, 10, 10, 10, 10, 17, 12, 10], + dtype=np.dtype("f2"), + ) + third_arr = np.array( + [10, 30, 20, 21, 15, 8, 10, 37, 21, 77, 20, 28, 25, 10, 9, 10, 15, 9, 10], + dtype=np.dtype("f2"), + ) + a = Trace(first_arr) + b = Trace(second_arr) + c = Trace(third_arr) + result = align_dtw_scale(a, b, c) + + assert np.argmax(result[0].samples) == np.argmax(result[1].samples) + assert np.argmax(result[1].samples) == np.argmax(result[2].samples) + plot(*result) + + result_other = align_dtw_scale(a, b, c, fast=False) + + assert np.argmax(result_other[0].samples) == np.argmax(result_other[1].samples) + assert np.argmax(result_other[1].samples) == np.argmax(result_other[2].samples) + plot(*result_other) diff --git a/test/sca/test_combine.py b/test/sca/test_combine.py index 953b4bf..7e62780 100644 --- a/test/sca/test_combine.py +++ b/test/sca/test_combine.py @@ -1,6 +1,7 @@ -from unittest import TestCase - +from collections import namedtuple import numpy as np +import pytest + from pyecsca.sca import ( Trace, CombinedTrace, @@ -14,65 +15,68 @@ from pyecsca.sca import ( ) -class CombineTests(TestCase): - def setUp(self): - self.a = Trace(np.array([20, 80], dtype=np.dtype("i1")), {"data": b"\xff"}) - self.b = Trace(np.array([30, 42], dtype=np.dtype("i1")), {"data": b"\xff"}) - self.c = Trace(np.array([78, 56], dtype=np.dtype("i1")), {"data": b"\x00"}) +@pytest.fixture() +def data(): + Data = namedtuple("Data", ["a", "b", "c"]) + return Data(a=Trace(np.array([20, 80], dtype=np.dtype("i1")), {"data": b"\xff"}), + b=Trace(np.array([30, 42], dtype=np.dtype("i1")), {"data": b"\xff"}), + c=Trace(np.array([78, 56], dtype=np.dtype("i1")), {"data": b"\x00"})) + + +def test_average(data): + assert average() is None + result = average(data.a, data.b) + assert result is not None + assert isinstance(result, CombinedTrace) + assert len(result.samples) == 2 + assert result.samples[0] == 25 + assert result.samples[1] == 61 + + +def test_conditional_average(data): + result = conditional_average(data.a, data.b, data.c, condition=lambda trace: trace.meta["data"] == b"\xff", ) + assert isinstance(result, CombinedTrace) + assert len(result.samples) == 2 + assert result.samples[0] == 25 + assert result.samples[1] == 61 + + +def test_standard_deviation(data): + assert standard_deviation() is None + result = standard_deviation(data.a, data.b) + assert isinstance(result, CombinedTrace) + assert len(result.samples) == 2 + - def test_average(self): - self.assertIsNone(average()) - result = average(self.a, self.b) - self.assertIsNotNone(result) - self.assertIsInstance(result, CombinedTrace) - self.assertEqual(len(result.samples), 2) - self.assertEqual(result.samples[0], 25) - self.assertEqual(result.samples[1], 61) +def test_variance(data): + assert variance() is None + result = variance(data.a, data.b) + assert isinstance(result, CombinedTrace) + assert len(result.samples) == 2 - def test_conditional_average(self): - result = conditional_average( - self.a, - self.b, - self.c, - condition=lambda trace: trace.meta["data"] == b"\xff", - ) - self.assertIsInstance(result, CombinedTrace) - self.assertEqual(len(result.samples), 2) - self.assertEqual(result.samples[0], 25) - self.assertEqual(result.samples[1], 61) - def test_standard_deviation(self): - self.assertIsNone(standard_deviation()) - result = standard_deviation(self.a, self.b) - self.assertIsInstance(result, CombinedTrace) - self.assertEqual(len(result.samples), 2) +def test_average_and_variance(data): + assert average_and_variance() is None + mean, var = average_and_variance(data.a, data.b) + assert isinstance(mean, CombinedTrace) + assert isinstance(var, CombinedTrace) + assert len(mean.samples) == 2 + assert len(var.samples) == 2 + assert mean == average(data.a, data.b) + assert var == variance(data.a, data.b) - def test_variance(self): - self.assertIsNone(variance()) - result = variance(self.a, self.b) - self.assertIsInstance(result, CombinedTrace) - self.assertEqual(len(result.samples), 2) - def test_average_and_variance(self): - self.assertIsNone(average_and_variance()) - mean, var = average_and_variance(self.a, self.b) - self.assertIsInstance(mean, CombinedTrace) - self.assertIsInstance(var, CombinedTrace) - self.assertEqual(len(mean.samples), 2) - self.assertEqual(len(var.samples), 2) - self.assertEqual(mean, average(self.a, self.b)) - self.assertEqual(var, variance(self.a, self.b)) +def test_add(data): + assert add() is None + result = add(data.a, data.b) + assert isinstance(result, CombinedTrace) + assert result.samples[0] == 50 + assert result.samples[1] == 122 + np.testing.assert_equal(data.a.samples, add(data.a).samples) - def test_add(self): - self.assertIsNone(add()) - result = add(self.a, self.b) - self.assertIsInstance(result, CombinedTrace) - self.assertEqual(result.samples[0], 50) - self.assertEqual(result.samples[1], 122) - np.testing.assert_equal(self.a.samples, add(self.a).samples) - def test_subtract(self): - result = subtract(self.a, self.b) - self.assertIsInstance(result, CombinedTrace) - self.assertEqual(result.samples[0], -10) - self.assertEqual(result.samples[1], 38) +def test_subtract(data): + result = subtract(data.a, data.b) + assert isinstance(result, CombinedTrace) + assert result.samples[0] == -10 + assert result.samples[1] == 38 diff --git a/test/sca/test_edit.py b/test/sca/test_edit.py index 282e62e..db80393 100644 --- a/test/sca/test_edit.py +++ b/test/sca/test_edit.py @@ -1,48 +1,50 @@ -from unittest import TestCase - import numpy as np +import pytest from pyecsca.sca import Trace, trim, reverse, pad -class EditTests(TestCase): - def setUp(self): - self._trace = Trace(np.array([10, 20, 30, 40, 50], dtype=np.dtype("i1"))) +@pytest.fixture() +def trace(): + return Trace(np.array([10, 20, 30, 40, 50], dtype=np.dtype("i1"))) + + +def test_trim(trace): + result = trim(trace, 2) + assert result is not None + np.testing.assert_equal( + result.samples, np.array([30, 40, 50], dtype=np.dtype("i1")) + ) + + result = trim(trace, end=3) + assert result is not None + np.testing.assert_equal( + result.samples, np.array([10, 20, 30], dtype=np.dtype("i1")) + ) - def test_trim(self): - result = trim(self._trace, 2) - self.assertIsNotNone(result) - np.testing.assert_equal( - result.samples, np.array([30, 40, 50], dtype=np.dtype("i1")) - ) + with pytest.raises(ValueError): + trim(trace, 5, 1) - result = trim(self._trace, end=3) - self.assertIsNotNone(result) - np.testing.assert_equal( - result.samples, np.array([10, 20, 30], dtype=np.dtype("i1")) - ) - with self.assertRaises(ValueError): - trim(self._trace, 5, 1) +def test_reverse(trace): + result = reverse(trace) + assert result is not None + np.testing.assert_equal( + result.samples, np.array([50, 40, 30, 20, 10], dtype=np.dtype("i1")) + ) - def test_reverse(self): - result = reverse(self._trace) - self.assertIsNotNone(result) - np.testing.assert_equal( - result.samples, np.array([50, 40, 30, 20, 10], dtype=np.dtype("i1")) - ) - def test_pad(self): - result = pad(self._trace, 2) - self.assertIsNotNone(result) - np.testing.assert_equal( - result.samples, - np.array([0, 0, 10, 20, 30, 40, 50, 0, 0], dtype=np.dtype("i1")), - ) +def test_pad(trace): + result = pad(trace, 2) + assert result is not None + np.testing.assert_equal( + result.samples, + np.array([0, 0, 10, 20, 30, 40, 50, 0, 0], dtype=np.dtype("i1")), + ) - result = pad(self._trace, (1, 3)) - self.assertIsNotNone(result) - np.testing.assert_equal( - result.samples, - np.array([0, 10, 20, 30, 40, 50, 0, 0, 0], dtype=np.dtype("i1")), - ) + result = pad(trace, (1, 3)) + assert result is not None + np.testing.assert_equal( + result.samples, + np.array([0, 10, 20, 30, 40, 50, 0, 0, 0], dtype=np.dtype("i1")), + ) diff --git a/test/sca/test_filter.py b/test/sca/test_filter.py index 91f037c..d937f3a 100644 --- a/test/sca/test_filter.py +++ b/test/sca/test_filter.py @@ -1,4 +1,6 @@ import numpy as np +import pytest + from pyecsca.sca import ( Trace, filter_lowpass, @@ -6,39 +8,42 @@ from pyecsca.sca import ( filter_bandpass, filter_bandstop, ) -from .utils import Plottable -class FilterTests(Plottable): - def setUp(self): - self._trace = Trace( - np.array( - [5, 12, 15, 13, 15, 11, 7, 2, -4, -8, -10, -8, -13, -9, -11, -8, -5], - dtype=np.dtype("i1"), - ), - None, - ) +@pytest.fixture() +def trace(): + return Trace( + np.array( + [5, 12, 15, 13, 15, 11, 7, 2, -4, -8, -10, -8, -13, -9, -11, -8, -5], + dtype=np.dtype("i1"), + ), + None, + ) + + +def test_lowpass(trace, plot): + result = filter_lowpass(trace, 100, 20) + assert result is not None + assert len(trace.samples) == len(result.samples) + plot(trace, result) + + +def test_highpass(trace, plot): + result = filter_highpass(trace, 128, 20) + assert result is not None + assert len(trace.samples) == len(result.samples) + plot(trace, result) - def test_lowpass(self): - result = filter_lowpass(self._trace, 100, 20) - self.assertIsNotNone(result) - self.assertEqual(len(self._trace.samples), len(result.samples)) - self.plot(self._trace, result) - def test_highpass(self): - result = filter_highpass(self._trace, 128, 20) - self.assertIsNotNone(result) - self.assertEqual(len(self._trace.samples), len(result.samples)) - self.plot(self._trace, result) +def test_bandpass(trace, plot): + result = filter_bandpass(trace, 128, 20, 60) + assert result is not None + assert len(trace.samples) == len(result.samples) + plot(trace, result) - def test_bandpass(self): - result = filter_bandpass(self._trace, 128, 20, 60) - self.assertIsNotNone(result) - self.assertEqual(len(self._trace.samples), len(result.samples)) - self.plot(self._trace, result) - def test_bandstop(self): - result = filter_bandstop(self._trace, 128, 20, 60) - self.assertIsNotNone(result) - self.assertEqual(len(self._trace.samples), len(result.samples)) - self.plot(self._trace, result) +def test_bandstop(trace, plot): + result = filter_bandstop(trace, 128, 20, 60) + assert result is not None + assert len(trace.samples) == len(result.samples) + plot(trace, result) diff --git a/test/sca/test_leakage_models.py b/test/sca/test_leakage_models.py index e9da42c..8be5704 100644 --- a/test/sca/test_leakage_models.py +++ b/test/sca/test_leakage_models.py @@ -1,119 +1,101 @@ -from unittest import TestCase - from pyecsca.ec.context import local, DefaultContext from pyecsca.ec.formula import FormulaAction, OpResult from pyecsca.ec.mod import Mod from pyecsca.ec.mult import LTRMultiplier from pyecsca.ec.op import OpType -from pyecsca.ec.params import get_params from pyecsca.sca.attack.leakage_model import Identity, Bit, Slice, HammingWeight, HammingDistance, BitLength +import pytest -class LeakageModelTests(TestCase): +def test_identity(): + val = Mod(3, 7) + lm = Identity() + assert lm(val) == 3 - def test_identity(self): - val = Mod(3, 7) - lm = Identity() - self.assertEqual(lm(val), 3) - def test_bit(self): - val = Mod(3, 7) - lm = Bit(0) - self.assertEqual(lm(val), 1) - lm = Bit(4) - self.assertEqual(lm(val), 0) - with self.assertRaises(ValueError): - Bit(-3) +def test_bit(): + val = Mod(3, 7) + lm = Bit(0) + assert lm(val) == 1 + lm = Bit(4) + assert lm(val) == 0 + with pytest.raises(ValueError): + Bit(-3) - def test_slice(self): - val = Mod(0b11110000, 0xf00) - lm = Slice(0, 4) - self.assertEqual(lm(val), 0) - lm = Slice(1, 5) - self.assertEqual(lm(val), 0b1000) - lm = Slice(4, 8) - self.assertEqual(lm(val), 0b1111) - with self.assertRaises(ValueError): - Slice(7, 1) - def test_hamming_weight(self): - val = Mod(0b11110000, 0xf00) - lm = HammingWeight() - self.assertEqual(lm(val), 4) +def test_slice(): + val = Mod(0b11110000, 0xf00) + lm = Slice(0, 4) + assert lm(val) == 0 + lm = Slice(1, 5) + assert lm(val) == 0b1000 + lm = Slice(4, 8) + assert lm(val) == 0b1111 + with pytest.raises(ValueError): + Slice(7, 1) - def test_hamming_distance(self): - a = Mod(0b11110000, 0xf00) - b = Mod(0b00010000, 0xf00) - lm = HammingDistance() - self.assertEqual(lm(a, b), 3) - def test_bit_length(self): - a = Mod(0b11110000, 0xf00) - lm = BitLength() - self.assertEqual(lm(a), 8) +def test_hamming_weight(): + val = Mod(0b11110000, 0xf00) + lm = HammingWeight() + assert lm(val) == 4 -class ModelTraceTests(TestCase): +def test_hamming_distance(): + a = Mod(0b11110000, 0xf00) + b = Mod(0b00010000, 0xf00) + lm = HammingDistance() + assert lm(a, b) == 3 - def setUp(self): - self.secp128r1 = get_params("secg", "secp128r1", "projective") - self.base = self.secp128r1.generator - self.coords = self.secp128r1.curve.coordinate_model - self.add = self.coords.formulas["add-1998-cmo"] - self.dbl = self.coords.formulas["dbl-1998-cmo"] - self.neg = self.coords.formulas["neg"] - self.scale = self.coords.formulas["z"] - def test_mult_hw(self): - scalar = 0x123456789 - mult = LTRMultiplier( - self.add, - self.dbl, - self.scale, - always=True, - complete=False, - short_circuit=True, - ) - with local(DefaultContext()) as ctx: - mult.init(self.secp128r1, self.base) - mult.multiply(scalar) +def test_bit_length(): + a = Mod(0b11110000, 0xf00) + lm = BitLength() + assert lm(a) == 8 - lm = HammingWeight() - trace = [] - def callback(action): - if isinstance(action, FormulaAction): - for intermediate in action.op_results: - leak = lm(intermediate.value) - trace.append(leak) +@pytest.fixture() +def context(secp128r1): + scalar = 0x123456789 + mult = LTRMultiplier( + secp128r1.curve.coordinate_model.formulas["add-1998-cmo"], + secp128r1.curve.coordinate_model.formulas["dbl-1998-cmo"], + secp128r1.curve.coordinate_model.formulas["z"], + always=True, + complete=False, + short_circuit=True, + ) + with local(DefaultContext()) as ctx: + mult.init(secp128r1, secp128r1.generator) + mult.multiply(scalar) + return ctx + - ctx.actions.walk(callback) - self.assertGreater(len(trace), 0) +def test_mult_hw(context): + lm = HammingWeight() + trace = [] - def test_mult_hd(self): - scalar = 0x123456789 - mult = LTRMultiplier( - self.add, - self.dbl, - self.scale, - always=True, - complete=False, - short_circuit=True, - ) - with local(DefaultContext()) as ctx: - mult.init(self.secp128r1, self.base) - mult.multiply(scalar) + def callback(action): + if isinstance(action, FormulaAction): + for intermediate in action.op_results: + leak = lm(intermediate.value) + trace.append(leak) - lm = HammingDistance() - trace = [] + context.actions.walk(callback) + assert len(trace) > 0 - def callback(action): - if isinstance(action, FormulaAction): - for intermediate in action.op_results: - if intermediate.op == OpType.Mult: - values = list(map(lambda v: v.value if isinstance(v, OpResult) else v, intermediate.parents)) - leak = lm(*values) - trace.append(leak) - ctx.actions.walk(callback) - self.assertGreater(len(trace), 0) +def test_mult_hd(context): + lm = HammingDistance() + trace = [] + + def callback(action): + if isinstance(action, FormulaAction): + for intermediate in action.op_results: + if intermediate.op == OpType.Mult: + values = list(map(lambda v: v.value if isinstance(v, OpResult) else v, intermediate.parents)) + leak = lm(*values) + trace.append(leak) + + context.actions.walk(callback) + assert len(trace) > 0 diff --git a/test/sca/test_match.py b/test/sca/test_match.py index d39e8c3..03d11c4 100644 --- a/test/sca/test_match.py +++ b/test/sca/test_match.py @@ -1,72 +1,71 @@ import numpy as np from pyecsca.sca import Trace, match_pattern, match_part, pad -from .utils import Plottable -class MatchingTests(Plottable): - def test_simple_match(self): - pattern = Trace( - np.array([1, 15, 12, -10, 0, 13, 17, -1, 0], dtype=np.dtype("i1")), None - ) - base = Trace( - np.array( - [0, 1, 3, 1, 2, -2, -3, 1, 15, 12, -10, 0, 13, 17, -1, 0, 3, 1], - dtype=np.dtype("i1"), - ), - None, - ) - filtered = match_part(base, 7, 9) - self.assertListEqual(filtered, [7]) - self.plot(base=base, pattern=pad(pattern, (filtered[0], 0))) +def test_simple_match(plot): + pattern = Trace( + np.array([1, 15, 12, -10, 0, 13, 17, -1, 0], dtype=np.dtype("i1")), None + ) + base = Trace( + np.array( + [0, 1, 3, 1, 2, -2, -3, 1, 15, 12, -10, 0, 13, 17, -1, 0, 3, 1], + dtype=np.dtype("i1"), + ), + None, + ) + filtered = match_part(base, 7, 9) + assert filtered == [7] + plot(base=base, pattern=pad(pattern, (filtered[0], 0))) - def test_multiple_match(self): - pattern = Trace( - np.array([1, 15, 12, -10, 0, 13, 17, -1, 0], dtype=np.dtype("i1")), None - ) - base = Trace( - np.array( - [ - 0, - 1, - 3, - 1, - 2, - -2, - -3, - 1, - 18, - 10, - -5, - 0, - 13, - 17, - -1, - 0, - 3, - 1, - 2, - 5, - 13, - 8, - -8, - 1, - 11, - 15, - 0, - 1, - 5, - 2, - 4, - ], - dtype=np.dtype("i1"), - ), - None, - ) - filtered = match_pattern(base, pattern, 0.9) - self.assertListEqual(filtered, [7, 19]) - self.plot( - base=base, - pattern1=pad(pattern, (filtered[0], 0)), - pattern2=pad(pattern, (filtered[1], 0)), - ) + +def test_multiple_match(plot): + pattern = Trace( + np.array([1, 15, 12, -10, 0, 13, 17, -1, 0], dtype=np.dtype("i1")), None + ) + base = Trace( + np.array( + [ + 0, + 1, + 3, + 1, + 2, + -2, + -3, + 1, + 18, + 10, + -5, + 0, + 13, + 17, + -1, + 0, + 3, + 1, + 2, + 5, + 13, + 8, + -8, + 1, + 11, + 15, + 0, + 1, + 5, + 2, + 4, + ], + dtype=np.dtype("i1"), + ), + None, + ) + filtered = match_pattern(base, pattern, 0.9) + assert filtered == [7, 19] + plot( + base=base, + pattern1=pad(pattern, (filtered[0], 0)), + pattern2=pad(pattern, (filtered[1], 0)), + ) diff --git a/test/sca/test_plot.py b/test/sca/test_plot.py index 7d2cec0..2722ba3 100644 --- a/test/sca/test_plot.py +++ b/test/sca/test_plot.py @@ -1,8 +1,8 @@ -from os import getenv - import numpy as np import holoviews as hv import matplotlib as mpl +import pytest + from pyecsca.sca.trace import Trace from pyecsca.sca.trace.plot import ( plot_trace, @@ -11,34 +11,35 @@ from pyecsca.sca.trace.plot import ( save_figure_svg, plot_traces, ) -from .utils import Plottable -class PlotTests(Plottable): - def setUp(self) -> None: - self.trace1 = Trace(np.array([6, 7, 3, -2, 5, 1], dtype=np.dtype("i1"))) - self.trace2 = Trace(np.array([2, 3, 7, 0, -1, 0], dtype=np.dtype("i1"))) +@pytest.fixture() +def trace1(): + return Trace(np.array([6, 7, 3, -2, 5, 1], dtype=np.dtype("i1"))) + + +@pytest.fixture() +def trace2(): + return Trace(np.array([2, 3, 7, 0, -1, 0], dtype=np.dtype("i1"))) + + +def test_html(trace1, trace2, plot_path): + hv.extension("bokeh") + fig = plot_trace(trace1) + save_figure(fig, str(plot_path())) + other = plot_traces(trace1, trace2) + save_figure(other, str(plot_path())) + - def test_html(self): - if getenv("PYECSCA_TEST_PLOTS") is None: - return - hv.extension("bokeh") - fig = plot_trace(self.trace1) - save_figure(fig, self.get_fname()) - other = plot_traces(self.trace1, self.trace2) - save_figure(other, self.get_fname()) +@pytest.mark.skip("Broken") +def test_png(trace1, plot_path): + hv.extension("matplotlib") + mpl.use("agg") + fig = plot_trace(trace1) + save_figure_png(fig, str(plot_path())) - def test_png(self): - if getenv("PYECSCA_TEST_PLOTS") is None: - return - hv.extension("matplotlib") - mpl.use("agg") - fig = plot_trace(self.trace1) - save_figure_png(fig, self.get_fname()) - def test_svg(self): - if getenv("PYECSCA_TEST_PLOTS") is None: - return - hv.extension("matplotlib") - fig = plot_trace(self.trace1) - save_figure_svg(fig, self.get_fname()) +def test_svg(trace1, plot_path): + hv.extension("matplotlib") + fig = plot_trace(trace1) + save_figure_svg(fig, str(plot_path())) diff --git a/test/sca/test_process.py b/test/sca/test_process.py index fda8575..cf9fbbd 100644 --- a/test/sca/test_process.py +++ b/test/sca/test_process.py @@ -1,6 +1,6 @@ -from unittest import TestCase - import numpy as np +import pytest + from pyecsca.sca import ( Trace, absolute, @@ -14,48 +14,56 @@ from pyecsca.sca import ( ) -class ProcessTests(TestCase): - def setUp(self): - self._trace = Trace(np.array([30, -60, 145, 247], dtype=np.dtype("i2")), None) +@pytest.fixture() +def trace(): + return Trace(np.array([30, -60, 145, 247], dtype=np.dtype("i2")), None) + + +def test_absolute(trace): + result = absolute(trace) + assert result is not None + assert result.samples[1] == 60 + + +def test_invert(trace): + result = invert(trace) + assert result is not None + np.testing.assert_equal(result.samples, [-30, 60, -145, -247]) + + +def test_threshold(trace): + result = threshold(trace, 128) + assert result is not None + assert result.samples[0] == 0 + assert result.samples[2] == 1 + + +def test_rolling_mean(trace): + result = rolling_mean(trace, 2) + assert result is not None + assert len(result.samples) == 3 + assert result.samples[0] == -15 + assert result.samples[1] == 42 + assert result.samples[2] == 196 - def test_absolute(self): - result = absolute(self._trace) - self.assertIsNotNone(result) - self.assertEqual(result.samples[1], 60) - def test_invert(self): - result = invert(self._trace) - self.assertIsNotNone(result) - np.testing.assert_equal(result.samples, [-30, 60, -145, -247]) +def test_offset(trace): + result = offset(trace, 5) + assert result is not None + np.testing.assert_equal( + result.samples, np.array([35, -55, 150, 252], dtype=np.dtype("i2")) + ) - def test_threshold(self): - result = threshold(self._trace, 128) - self.assertIsNotNone(result) - self.assertEqual(result.samples[0], 0) - self.assertEqual(result.samples[2], 1) - def test_rolling_mean(self): - result = rolling_mean(self._trace, 2) - self.assertIsNotNone(result) - self.assertEqual(len(result.samples), 3) - self.assertEqual(result.samples[0], -15) - self.assertEqual(result.samples[1], 42) - self.assertEqual(result.samples[2], 196) +def test_recenter(trace): + assert recenter(trace) is not None - def test_offset(self): - result = offset(self._trace, 5) - self.assertIsNotNone(result) - np.testing.assert_equal( - result.samples, np.array([35, -55, 150, 252], dtype=np.dtype("i2")) - ) - def test_recenter(self): - self.assertIsNotNone(recenter(self._trace)) +def test_normalize(trace): + result = normalize(trace) + assert result is not None - def test_normalize(self): - result = normalize(self._trace) - self.assertIsNotNone(result) - def test_normalize_wl(self): - result = normalize_wl(self._trace) - self.assertIsNotNone(result) +def test_normalize_wl(trace): + result = normalize_wl(trace) + assert result is not None diff --git a/test/sca/test_rpa.py b/test/sca/test_rpa.py index 995099d..f5dc7cc 100644 --- a/test/sca/test_rpa.py +++ b/test/sca/test_rpa.py @@ -1,94 +1,95 @@ -from unittest import TestCase +import io +from contextlib import redirect_stdout -from parameterized import parameterized +import pytest from pyecsca.ec.context import local +from pyecsca.ec.model import ShortWeierstrassModel +from pyecsca.ec.curve import EllipticCurve +from pyecsca.ec.mod import Mod from pyecsca.ec.mult import ( LTRMultiplier, + RTLMultiplier, BinaryNAFMultiplier, WindowNAFMultiplier, - LadderMultiplier, - DifferentialLadderMultiplier, + SimpleLadderMultiplier, ) -from pyecsca.ec.params import get_params -from pyecsca.sca.re.rpa import MultipleContext +from pyecsca.ec.params import DomainParameters +from pyecsca.ec.point import Point +from pyecsca.sca.re.rpa import MultipleContext, rpa_point_0y, rpa_point_x0, rpa_distinguish -class MultipleContextTests(TestCase): - def setUp(self): - self.secp128r1 = get_params("secg", "secp128r1", "projective") - self.base = self.secp128r1.generator - self.coords = self.secp128r1.curve.coordinate_model - self.add = self.coords.formulas["add-1998-cmo"] - self.dbl = self.coords.formulas["dbl-1998-cmo"] - self.neg = self.coords.formulas["neg"] - self.scale = self.coords.formulas["z"] +@pytest.fixture() +def model(): + return ShortWeierstrassModel() - @parameterized.expand( - [ - ("5", 5), - ("10", 10), - ("2355498743", 2355498743), - ( - "325385790209017329644351321912443757746", - 325385790209017329644351321912443757746, - ), - ("13613624287328732", 13613624287328732), - ] - ) - def test_basic(self, name, scalar): - mult = LTRMultiplier( - self.add, - self.dbl, - self.scale, - always=False, - complete=False, - short_circuit=True, - ) - with local(MultipleContext()) as ctx: - mult.init(self.secp128r1, self.base) - mult.multiply(scalar) - muls = list(ctx.points.values()) - self.assertEqual(muls[-1], scalar) - def test_precomp(self): - bnaf = BinaryNAFMultiplier(self.add, self.dbl, self.neg, self.scale) - with local(MultipleContext()) as ctx: - bnaf.init(self.secp128r1, self.base) - muls = list(ctx.points.values()) - self.assertListEqual(muls, [1, -1]) +@pytest.fixture() +def coords(model): + return model.coordinates["projective"] - wnaf = WindowNAFMultiplier(self.add, self.dbl, self.neg, 3, self.scale) - with local(MultipleContext()) as ctx: - wnaf.init(self.secp128r1, self.base) - muls = list(ctx.points.values()) - self.assertListEqual(muls, [1, 2, 3, 5]) - def test_window(self): - mult = WindowNAFMultiplier( - self.add, self.dbl, self.neg, 3, precompute_negation=True - ) - with local(MultipleContext()): - mult.init(self.secp128r1, self.base) - mult.multiply(5) +@pytest.fixture() +def add(coords): + return coords.formulas["add-2007-bl"] - def test_ladder(self): - curve25519 = get_params("other", "Curve25519", "xz") - base = curve25519.generator - coords = curve25519.curve.coordinate_model - ladd = coords.formulas["ladd-1987-m"] - dadd = coords.formulas["dadd-1987-m"] - dbl = coords.formulas["dbl-1987-m"] - scale = coords.formulas["scale"] - ladd_mult = LadderMultiplier(ladd, dbl, scale) - with local(MultipleContext()) as ctx: - ladd_mult.init(curve25519, base) - ladd_mult.multiply(1339278426732672313) - muls = list(ctx.points.values()) - self.assertEqual(muls[-2], 1339278426732672313) - dadd_mult = DifferentialLadderMultiplier(dadd, dbl, scale) - with local(MultipleContext()) as ctx: - dadd_mult.init(curve25519, base) - dadd_mult.multiply(1339278426732672313) - muls = list(ctx.points.values()) - self.assertEqual(muls[-2], 1339278426732672313) + +@pytest.fixture() +def dbl(coords): + return coords.formulas["dbl-2007-bl"] + + +@pytest.fixture() +def neg(coords): + return coords.formulas["neg"] + + +@pytest.fixture() +def rpa_params(model, coords): + p = 0x85d265945a4f5681 + a = Mod(0x7fc57b4110698bc0, p) + b = Mod(0x37113ea591b04527, p) + gx = Mod(0x80d2d78fddb97597, p) + gy = Mod(0x5586d818b7910930, p) + # (0x4880bcf620852a54, 0) RPA point + # (0, 0x6bed3155c9ada064) RPA point + + infty = Point(coords, X=Mod(0, p), Y=Mod(1, p), Z=Mod(0, p)) + g = Point(coords, X=gx, Y=gy, Z=Mod(1, p)) + curve = EllipticCurve(model, coords, p, infty, dict(a=a, b=b)) + return DomainParameters(curve, g, 0x85d265932d90785c, 1) + + +def test_x0_point(rpa_params): + res = rpa_point_x0(rpa_params) + assert res is not None + assert res.y == 0 + + +def test_0y_point(rpa_params): + res = rpa_point_0y(rpa_params) + assert res is not None + assert res.x == 0 + + +def test_distinguish(secp128r1, add, dbl, neg): + multipliers = [LTRMultiplier(add, dbl, None, False, True, True), + LTRMultiplier(add, dbl, None, True, True, True), + RTLMultiplier(add, dbl, None, False, True), + RTLMultiplier(add, dbl, None, True, True), + SimpleLadderMultiplier(add, dbl, None, True, True), + BinaryNAFMultiplier(add, dbl, neg, None, True), + WindowNAFMultiplier(add, dbl, neg, 3, None, True), + WindowNAFMultiplier(add, dbl, neg, 4, None, True)] + for real_mult in multipliers: + def simulated_oracle(scalar, affine_point): + point = affine_point.to_model(secp128r1.curve.coordinate_model, secp128r1.curve) + with local(MultipleContext()) as ctx: + real_mult.init(secp128r1, point) + real_mult.multiply(scalar) + return any(map(lambda P: P.X == 0 or P.Y == 0, ctx.points.keys())) + + with redirect_stdout(io.StringIO()): + result = rpa_distinguish(secp128r1, multipliers, simulated_oracle) + assert 1 == len(result) + assert real_mult == result[0] diff --git a/test/sca/test_rpa_context.py b/test/sca/test_rpa_context.py new file mode 100644 index 0000000..78191bc --- /dev/null +++ b/test/sca/test_rpa_context.py @@ -0,0 +1,106 @@ +from typing import cast + +import pytest + +from pyecsca.ec.context import local +from pyecsca.ec.formula import LadderFormula, DifferentialAdditionFormula, DoublingFormula, \ + ScalingFormula +from pyecsca.ec.mult import ( + LTRMultiplier, + BinaryNAFMultiplier, + WindowNAFMultiplier, + LadderMultiplier, + DifferentialLadderMultiplier +) +from pyecsca.sca.re.rpa import MultipleContext + + +@pytest.fixture() +def add(secp128r1): + return secp128r1.curve.coordinate_model.formulas["add-1998-cmo"] + + +@pytest.fixture() +def dbl(secp128r1): + return secp128r1.curve.coordinate_model.formulas["dbl-1998-cmo"] + + +@pytest.fixture() +def neg(secp128r1): + return secp128r1.curve.coordinate_model.formulas["neg"] + + +@pytest.fixture() +def scale(secp128r1): + return secp128r1.curve.coordinate_model.formulas["z"] + + +@pytest.mark.parametrize("name,scalar", + [ + ("5", 5), + ("10", 10), + ("2355498743", 2355498743), + ( + "325385790209017329644351321912443757746", + 325385790209017329644351321912443757746, + ), + ("13613624287328732", 13613624287328732), + ]) +def test_basic(secp128r1, add, dbl, scale, name, scalar): + mult = LTRMultiplier( + add, + dbl, + scale, + always=False, + complete=False, + short_circuit=True, + ) + with local(MultipleContext()) as ctx: + mult.init(secp128r1, secp128r1.generator) + mult.multiply(scalar) + muls = list(ctx.points.values()) + assert muls[-1] == scalar + + +def test_precomp(secp128r1, add, dbl, neg, scale): + bnaf = BinaryNAFMultiplier(add, dbl, neg, scale) + with local(MultipleContext()) as ctx: + bnaf.init(secp128r1, secp128r1.generator) + muls = list(ctx.points.values()) + assert muls == [1, -1] + + wnaf = WindowNAFMultiplier(add, dbl, neg, 3, scale) + with local(MultipleContext()) as ctx: + wnaf.init(secp128r1, secp128r1.generator) + muls = list(ctx.points.values()) + assert muls == [1, 2, 3, 5] + + +def test_window(secp128r1, add, dbl, neg): + mult = WindowNAFMultiplier( + add, dbl, neg, 3, precompute_negation=True + ) + with local(MultipleContext()): + mult.init(secp128r1, secp128r1.generator) + mult.multiply(5) + + +def test_ladder(curve25519): + base = curve25519.generator + coords = curve25519.curve.coordinate_model + ladd = cast(LadderFormula, coords.formulas["ladd-1987-m"]) + dadd = cast(DifferentialAdditionFormula, coords.formulas["dadd-1987-m"]) + dbl = cast(DoublingFormula, coords.formulas["dbl-1987-m"]) + scale = cast(ScalingFormula, coords.formulas["scale"]) + ladd_mult = LadderMultiplier(ladd, dbl, scale) + with local(MultipleContext()) as ctx: + ladd_mult.init(curve25519, base) + ladd_mult.multiply(1339278426732672313) + muls = list(ctx.points.values()) + assert muls[-2] == 1339278426732672313 + dadd_mult = DifferentialLadderMultiplier(dadd, dbl, scale) + with local(MultipleContext()) as ctx: + dadd_mult.init(curve25519, base) + dadd_mult.multiply(1339278426732672313) + muls = list(ctx.points.values()) + assert muls[-2] == 1339278426732672313 diff --git a/test/sca/test_sampling.py b/test/sca/test_sampling.py index fcebf2d..062e726 100644 --- a/test/sca/test_sampling.py +++ b/test/sca/test_sampling.py @@ -7,162 +7,159 @@ from pyecsca.sca import ( downsample_max, downsample_min, ) -from .utils import Plottable -class SamplingTests(Plottable): - def setUp(self): - self._trace = Trace(np.array([20, 40, 50, 50, 10], dtype=np.dtype("i1"))) +def test_downsample_average(): + trace = Trace(np.array([20, 40, 50, 50, 10], dtype=np.dtype("i1"))) + result = downsample_average(trace, 2) + assert result is not None + assert isinstance(result, Trace) + assert len(result.samples) == 2 + assert result.samples[0] == 30 + assert result.samples[1] == 50 - def test_downsample_average(self): - result = downsample_average(self._trace, 2) - self.assertIsNotNone(result) - self.assertIsInstance(result, Trace) - self.assertEqual(len(result.samples), 2) - self.assertEqual(result.samples[0], 30) - self.assertEqual(result.samples[1], 50) - def test_downsample_pick(self): - result = downsample_pick(self._trace, 2) - self.assertIsNotNone(result) - self.assertIsInstance(result, Trace) - self.assertEqual(len(result.samples), 3) - self.assertEqual(result.samples[0], 20) - self.assertEqual(result.samples[1], 50) +def test_downsample_pick(): + trace = Trace(np.array([20, 40, 50, 50, 10], dtype=np.dtype("i1"))) + result = downsample_pick(trace, 2) + assert result is not None + assert isinstance(result, Trace) + assert len(result.samples) == 3 + assert result.samples[0] == 20 + assert result.samples[1] == 50 - def test_downsample_max(self): - trace = Trace( - np.array( - [ - 20, - 30, - 55, - 18, - 15, - 10, - 35, - 24, - 21, - 15, - 10, - 8, - -10, - -5, - -8, - -12, - -15, - -18, - -34, - -21, - -17, - -10, - -5, - -12, - -6, - -2, - 4, - 8, - 21, - 28, - ], - dtype=np.dtype("i1"), - ) - ) - result = downsample_max(trace, 2) - self.assertIsNotNone(result) - self.assertIsInstance(result, Trace) - self.assertEqual(len(result.samples), 15) - self.assertEqual( - list(result), [30, 55, 15, 35, 21, 10, -5, -8, -15, -21, -10, -5, -2, 8, 28] - ) - def test_downsample_min(self): - trace = Trace( - np.array( - [ - 20, - 30, - 55, - 18, - 15, - 10, - 35, - 24, - 21, - 15, - 10, - 8, - -10, - -5, - -8, - -12, - -15, - -18, - -34, - -21, - -17, - -10, - -5, - -12, - -6, - -2, - 4, - 8, - 21, - 28, - ], - dtype=np.dtype("i1"), - ) +def test_downsample_max(): + trace = Trace( + np.array( + [ + 20, + 30, + 55, + 18, + 15, + 10, + 35, + 24, + 21, + 15, + 10, + 8, + -10, + -5, + -8, + -12, + -15, + -18, + -34, + -21, + -17, + -10, + -5, + -12, + -6, + -2, + 4, + 8, + 21, + 28, + ], + dtype=np.dtype("i1"), ) - result = downsample_min(trace, 2) - self.assertIsNotNone(result) - self.assertIsInstance(result, Trace) - self.assertEqual(len(result.samples), 15) - self.assertEqual( - list(result), - [20, 18, 10, 24, 15, 8, -10, -12, -18, -34, -17, -12, -6, 4, 21], + ) + result = downsample_max(trace, 2) + assert result is not None + assert isinstance(result, Trace) + assert len(result.samples) == 15 + assert list(result) == [30, 55, 15, 35, 21, 10, -5, -8, -15, -21, -10, -5, -2, 8, 28] + + +def test_downsample_min(): + trace = Trace( + np.array( + [ + 20, + 30, + 55, + 18, + 15, + 10, + 35, + 24, + 21, + 15, + 10, + 8, + -10, + -5, + -8, + -12, + -15, + -18, + -34, + -21, + -17, + -10, + -5, + -12, + -6, + -2, + 4, + 8, + 21, + 28, + ], + dtype=np.dtype("i1"), ) + ) + result = downsample_min(trace, 2) + assert result is not None + assert isinstance(result, Trace) + assert len(result.samples) == 15 + assert list(result) == \ + [20, 18, 10, 24, 15, 8, -10, -12, -18, -34, -17, -12, -6, 4, 21] + - def test_downsample_decimate(self): - trace = Trace( - np.array( - [ - 20, - 30, - 55, - 18, - 15, - 10, - 35, - 24, - 21, - 15, - 10, - 8, - -10, - -5, - -8, - -12, - -15, - -18, - -34, - -21, - -17, - -10, - -5, - -12, - -6, - -2, - 4, - 8, - 21, - 28, - ], - dtype=np.dtype("i1"), - ) +def test_downsample_decimate(plot): + trace = Trace( + np.array( + [ + 20, + 30, + 55, + 18, + 15, + 10, + 35, + 24, + 21, + 15, + 10, + 8, + -10, + -5, + -8, + -12, + -15, + -18, + -34, + -21, + -17, + -10, + -5, + -12, + -6, + -2, + 4, + 8, + 21, + 28, + ], + dtype=np.dtype("i1"), ) - result = downsample_decimate(trace, 2) - self.assertIsNotNone(result) - self.assertIsInstance(result, Trace) - self.assertEqual(len(result.samples), 15) - self.plot(trace, result) + ) + result = downsample_decimate(trace, 2) + assert result is not None + assert isinstance(result, Trace) + assert len(result.samples) == 15 + plot(original=trace, result=result) diff --git a/test/sca/test_stacked_combine.py b/test/sca/test_stacked_combine.py index 6f8fe60..4ed8d35 100644 --- a/test/sca/test_stacked_combine.py +++ b/test/sca/test_stacked_combine.py @@ -1,4 +1,4 @@ -from unittest import TestCase +import pytest from numba import cuda import numpy as np @@ -15,98 +15,94 @@ TRACE_COUNT = 32 TRACE_LEN = 4 * TPB -class StackedCombineTests(TestCase): - def setUp(self): - if not cuda.is_available(): - self.skipTest("CUDA not available") - self.samples = np.random.rand(TRACE_COUNT, TRACE_LEN) - self.stacked_ts = StackedTraces(self.samples) - self.gpu_manager = GPUTraceManager(self.stacked_ts, TPB) +@pytest.fixture() +def samples(): + np.random.seed(0x1234) + return np.random.rand(TRACE_COUNT, TRACE_LEN) - def test_fromarray(self): - max_len = self.samples.shape[1] - min_len = max_len // 2 - jagged_samples = [ - t[min_len:np.random.randint(max_len)] - for t - in self.samples - ] - min_len = min(map(len, jagged_samples)) - stacked = StackedTraces.fromarray(jagged_samples) - self.assertIsInstance(stacked, StackedTraces) - self.assertTupleEqual( - stacked.samples.shape, - (self.samples.shape[0], min_len) - ) - self.assertTrue((stacked.samples == self.samples[:, :min_len]).all()) +@pytest.fixture() +def gpu_manager(samples): + if not cuda.is_available(): + pytest.skip("CUDA not available") + return GPUTraceManager(StackedTraces(samples), TPB) - def test_fromtraceset(self): - max_len = self.samples.shape[1] - min_len = max_len // 2 - traces = [ - Trace(t[min_len:np.random.randint(max_len)]) - for t - in self.samples - ] - tset = TraceSet(*traces) - min_len = min(map(len, traces)) - stacked = StackedTraces.fromtraceset(tset) - self.assertIsInstance(stacked, StackedTraces) - self.assertTupleEqual( - stacked.samples.shape, - (self.samples.shape[0], min_len) - ) - self.assertTrue((stacked.samples == self.samples[:, :min_len]).all()) +def test_fromarray(samples): + max_len = samples.shape[1] + min_len = max_len // 2 + jagged_samples = [ + t[min_len:np.random.randint(max_len)] + for t + in samples + ] + min_len = min(map(len, jagged_samples)) + stacked = StackedTraces.fromarray(jagged_samples) - def test_average(self): - avg_trace = self.gpu_manager.average() - avg_cmp: np.ndarray = np.average(self.samples, 0) + assert isinstance(stacked, StackedTraces) + assert stacked.samples.shape == \ + (samples.shape[0], min_len) + assert (stacked.samples == samples[:, :min_len]).all() - self.assertIsInstance(avg_trace, CombinedTrace) - self.assertTupleEqual( - avg_trace.samples.shape, - avg_cmp.shape - ) - self.assertTrue(all(np.isclose(avg_trace.samples, avg_cmp))) - def test_standard_deviation(self): - std_trace = self.gpu_manager.standard_deviation() - std_cmp: np.ndarray = np.std(self.samples, 0) +def test_fromtraceset(samples): + max_len = samples.shape[1] + min_len = max_len // 2 + traces = [ + Trace(t[min_len:np.random.randint(max_len)]) + for t + in samples + ] + tset = TraceSet(*traces) + min_len = min(map(len, traces)) + stacked = StackedTraces.fromtraceset(tset) - self.assertIsInstance(std_trace, CombinedTrace) - self.assertTupleEqual( - std_trace.samples.shape, - std_cmp.shape - ) - self.assertTrue(all(np.isclose(std_trace.samples, std_cmp))) + assert isinstance(stacked, StackedTraces) + assert stacked.samples.shape == \ + (samples.shape[0], min_len) + assert (stacked.samples == samples[:, :min_len]).all() - def test_variance(self): - var_trace = self.gpu_manager.variance() - var_cmp: np.ndarray = np.var(self.samples, 0) - self.assertIsInstance(var_trace, CombinedTrace) - self.assertTupleEqual( - var_trace.samples.shape, - var_cmp.shape - ) - self.assertTrue(all(np.isclose(var_trace.samples, var_cmp))) +def test_average(samples, gpu_manager): + avg_trace = gpu_manager.average() + avg_cmp: np.ndarray = np.average(samples, 0) - def test_average_and_variance(self): - avg_trace, var_trace = self.gpu_manager.average_and_variance() - avg_cmp: np.ndarray = np.average(self.samples, 0) - var_cmp: np.ndarray = np.var(self.samples, 0) + assert isinstance(avg_trace, CombinedTrace) + assert avg_trace.samples.shape == \ + avg_cmp.shape + assert all(np.isclose(avg_trace.samples, avg_cmp)) - self.assertIsInstance(avg_trace, CombinedTrace) - self.assertIsInstance(var_trace, CombinedTrace) - self.assertTupleEqual( - avg_trace.samples.shape, - avg_cmp.shape - ) - self.assertTupleEqual( - var_trace.samples.shape, - var_cmp.shape - ) - self.assertTrue(all(np.isclose(avg_trace.samples, avg_cmp))) - self.assertTrue(all(np.isclose(var_trace.samples, var_cmp))) + +def test_standard_deviation(samples, gpu_manager): + std_trace = gpu_manager.standard_deviation() + std_cmp: np.ndarray = np.std(samples, 0) + + assert isinstance(std_trace, CombinedTrace) + assert std_trace.samples.shape == \ + std_cmp.shape + assert all(np.isclose(std_trace.samples, std_cmp)) + + +def test_variance(samples, gpu_manager): + var_trace = gpu_manager.variance() + var_cmp: np.ndarray = np.var(samples, 0) + + assert isinstance(var_trace, CombinedTrace) + assert var_trace.samples.shape == \ + var_cmp.shape + assert all(np.isclose(var_trace.samples, var_cmp)) + + +def test_average_and_variance(samples, gpu_manager): + avg_trace, var_trace = gpu_manager.average_and_variance() + avg_cmp: np.ndarray = np.average(samples, 0) + var_cmp: np.ndarray = np.var(samples, 0) + + assert isinstance(avg_trace, CombinedTrace) + assert isinstance(var_trace, CombinedTrace) + assert avg_trace.samples.shape == \ + avg_cmp.shape + assert var_trace.samples.shape == \ + var_cmp.shape + assert all(np.isclose(avg_trace.samples, avg_cmp)) + assert all(np.isclose(var_trace.samples, var_cmp)) diff --git a/test/sca/test_target.py b/test/sca/test_target.py index 299e127..4155b25 100644 --- a/test/sca/test_target.py +++ b/test/sca/test_target.py @@ -1,10 +1,12 @@ +import io +from contextlib import redirect_stdout from copy import copy -from os.path import realpath, dirname, join -from typing import Optional -from unittest import TestCase, SkipTest -from smartcard.pcsc.PCSCExceptions import BaseSCardException +import pytest +from importlib_resources import files, as_file +from smartcard.pcsc.PCSCExceptions import BaseSCardException +import test.data.sca from pyecsca.ec.key_agreement import ECDH_SHA1 from pyecsca.ec.key_generation import KeyGeneration from pyecsca.ec.mod import Mod @@ -31,461 +33,475 @@ from pyecsca.sca.target.ectester import ( ) if has_pyscard: - from pyecsca.sca.target.ectester import ECTesterTarget + from pyecsca.sca.target.ectester import ECTesterTargetPCSC as ECTesterTarget else: - ECTesterTarget = None + from pyecsca.sca.target.ectester import ECTesterTarget class TestTarget(SimpleSerialTarget, BinaryTarget): - pass + __test__ = False -class BinaryTargetTests(TestCase): - def test_basic_target(self): - target_path = join(dirname(realpath(__file__)), "..", "data", "target.py") +def test_basic_target(): + with as_file(files(test.data.sca).joinpath("target.py")) as target_path: target = TestTarget(["python", target_path]) target.connect() resp = target.send_cmd(SimpleSerialMessage("d", ""), 500) - self.assertIn("r", resp) - self.assertIn("z", resp) - self.assertEqual(resp["r"].data, "01020304") + assert "r" in resp + assert "z" in resp + assert resp["r"].data == "01020304" target.disconnect() - def test_debug(self): - target_path = join(dirname(realpath(__file__)), "..", "data", "target.py") + +def test_debug(): + with as_file(files(test.data.sca).joinpath("target.py")) as target_path: target = TestTarget(["python", target_path], debug_output=True) - target.connect() - target.send_cmd(SimpleSerialMessage("d", ""), 500) - target.disconnect() + with redirect_stdout(io.StringIO()) as out: + target.connect() + target.send_cmd(SimpleSerialMessage("d", ""), 500) + target.disconnect() + assert out.read() is not None + - def test_no_connection(self): - target_path = join(dirname(realpath(__file__)), "..", "data", "target.py") - target = TestTarget(target_path) - with self.assertRaises(ValueError): +def test_no_connection(): + with as_file(files(test.data.sca).joinpath("target.py")) as target_path: + target = TestTarget(str(target_path)) + with pytest.raises(ValueError): target.write(bytes([1, 2, 3, 4])) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): target.read(5) target.disconnect() -class ECTesterTargetTests(TestCase): - reader: Optional[str] = None - target: Optional[ECTesterTarget] = None - secp256r1: DomainParameters - secp256r1_projective: DomainParameters +@pytest.fixture() +def secp256r1_affine(): + return get_params("secg", "secp256r1", "affine") - @classmethod - def setUpClass(cls): - if not has_pyscard: - return - from smartcard.System import readers - try: - rs = readers() - except BaseSCardException: - return - if not rs: - return - cls.reader = rs[0] - cls.secp256r1 = get_params("secg", "secp256r1", "affine") - cls.secp256r1_projective = get_params("secg", "secp256r1", "projective") +@pytest.fixture() +def secp256r1_projective(): + return get_params("secg", "secp256r1", "projective") - def setUp(self): - if not ECTesterTargetTests.reader: - raise SkipTest("No smartcard readers.") - self.target = ECTesterTarget(ECTesterTargetTests.reader) - self.target.connect() - if not self.target.select_applet(): - self.target.disconnect() - raise SkipTest("No applet in reader: {}".format(ECTesterTargetTests.reader)) - def tearDown(self): - self.target.cleanup() - self.target.disconnect() +@pytest.fixture() +def target(): + if not has_pyscard: + pytest.skip("No pyscard.") + from smartcard.System import readers + rs = None + try: + rs = readers() + except BaseSCardException as e: + pytest.skip(f"No reader found: {e}") + if not rs: + pytest.skip("No reader found") + reader = rs[0] + target: ECTesterTarget = ECTesterTarget(reader) + target.connect() + if not target.select_applet(): + target.disconnect() + pytest.skip(f"No applet in reader: {reader}") + yield target + target.cleanup() + target.disconnect() - def test_allocate(self): - ka_resp = self.target.allocate_ka(KeyAgreementEnum.ALG_EC_SVDP_DH) - self.assertTrue(ka_resp.success) - sig_resp = self.target.allocate_sig(SignatureEnum.ALG_ECDSA_SHA) - self.assertTrue(sig_resp.success) - key_resp = self.target.allocate( - KeypairEnum.KEYPAIR_LOCAL, - KeyBuildEnum.BUILD_KEYPAIR, - 256, - KeyClassEnum.ALG_EC_FP, - ) - self.assertTrue(key_resp.success) - def test_set(self): - self.target.allocate( - KeypairEnum.KEYPAIR_LOCAL, - KeyBuildEnum.BUILD_KEYPAIR, - 256, - KeyClassEnum.ALG_EC_FP, - ) - set_resp = self.target.set( - KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP - ) - self.assertTrue(set_resp.success) +def test_allocate(target): + ka_resp = target.allocate_ka(KeyAgreementEnum.ALG_EC_SVDP_DH) + assert ka_resp.success + sig_resp = target.allocate_sig(SignatureEnum.ALG_ECDSA_SHA) + assert sig_resp.success + key_resp = target.allocate( + KeypairEnum.KEYPAIR_LOCAL, + KeyBuildEnum.BUILD_KEYPAIR, + 256, + KeyClassEnum.ALG_EC_FP, + ) + assert key_resp.success - def test_set_explicit(self): - self.target.allocate( - KeypairEnum.KEYPAIR_LOCAL, - KeyBuildEnum.BUILD_KEYPAIR, - 256, - KeyClassEnum.ALG_EC_FP, - ) - values = ECTesterTarget.encode_parameters( - ParameterEnum.DOMAIN_FP, self.secp256r1 - ) - set_resp = self.target.set( - KeypairEnum.KEYPAIR_LOCAL, - CurveEnum.external, - ParameterEnum.DOMAIN_FP, - values, - ) - self.assertTrue(set_resp.success) - def test_generate(self): - self.target.allocate( - KeypairEnum.KEYPAIR_LOCAL, - KeyBuildEnum.BUILD_KEYPAIR, - 256, - KeyClassEnum.ALG_EC_FP, - ) - self.target.set( - KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP - ) - generate_resp = self.target.generate(KeypairEnum.KEYPAIR_LOCAL) - self.assertTrue(generate_resp.success) +def test_set(target): + target.allocate( + KeypairEnum.KEYPAIR_LOCAL, + KeyBuildEnum.BUILD_KEYPAIR, + 256, + KeyClassEnum.ALG_EC_FP, + ) + set_resp = target.set( + KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP + ) + assert set_resp.success - def test_clear(self): - self.target.allocate( - KeypairEnum.KEYPAIR_LOCAL, - KeyBuildEnum.BUILD_KEYPAIR, - 256, - KeyClassEnum.ALG_EC_FP, - ) - clear_resp = self.target.clear(KeypairEnum.KEYPAIR_LOCAL) - self.assertTrue(clear_resp.success) - def test_cleanup(self): - cleanup_resp = self.target.cleanup() - self.assertTrue(cleanup_resp.success) +def test_set_explicit(target, secp256r1_affine): + target.allocate( + KeypairEnum.KEYPAIR_LOCAL, + KeyBuildEnum.BUILD_KEYPAIR, + 256, + KeyClassEnum.ALG_EC_FP, + ) + values = ECTesterTarget.encode_parameters( + ParameterEnum.DOMAIN_FP, secp256r1_affine + ) + set_resp = target.set( + KeypairEnum.KEYPAIR_LOCAL, + CurveEnum.external, + ParameterEnum.DOMAIN_FP, + values, + ) + assert set_resp.success - def test_info(self): - info_resp = self.target.info() - self.assertTrue(info_resp.success) - def test_dry_run(self): - dry_run_resp = self.target.run_mode(RunModeEnum.MODE_DRY_RUN) - self.assertTrue(dry_run_resp.success) - allocate_resp = self.target.allocate( - KeypairEnum.KEYPAIR_LOCAL, - KeyBuildEnum.BUILD_KEYPAIR, - 256, - KeyClassEnum.ALG_EC_FP, - ) - self.assertTrue(allocate_resp.success) - dry_run_resp = self.target.run_mode(RunModeEnum.MODE_NORMAL) - self.assertTrue(dry_run_resp.success) +def test_generate(target): + target.allocate( + KeypairEnum.KEYPAIR_LOCAL, + KeyBuildEnum.BUILD_KEYPAIR, + 256, + KeyClassEnum.ALG_EC_FP, + ) + target.set( + KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP + ) + generate_resp = target.generate(KeypairEnum.KEYPAIR_LOCAL) + assert generate_resp.success - def test_export(self): - self.target.allocate( - KeypairEnum.KEYPAIR_LOCAL, - KeyBuildEnum.BUILD_KEYPAIR, - 256, - KeyClassEnum.ALG_EC_FP, - ) - self.target.set( - KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP - ) - self.target.generate(KeypairEnum.KEYPAIR_LOCAL) - export_public_resp = self.target.export( - KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PUBLIC, ParameterEnum.W - ) - self.assertTrue(export_public_resp.success) - pubkey_bytes = export_public_resp.get_param( - KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.W - ) - pubkey = self.secp256r1.curve.decode_point(pubkey_bytes) - export_privkey_resp = self.target.export( - KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PRIVATE, ParameterEnum.S - ) - self.assertTrue(export_privkey_resp.success) - privkey = int.from_bytes( - export_privkey_resp.get_param(KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.S), - "big", - ) - self.assertEqual( - pubkey, - self.secp256r1.curve.affine_multiply(self.secp256r1.generator, privkey), - ) - def test_export_curve(self): - self.target.allocate( - KeypairEnum.KEYPAIR_LOCAL, - KeyBuildEnum.BUILD_KEYPAIR, - 256, - KeyClassEnum.ALG_EC_FP, - ) - self.target.set( - KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP - ) - export_resp = self.target.export( - KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PUBLIC, ParameterEnum.DOMAIN_FP - ) - self.assertTrue(export_resp.success) +def test_clear(target): + target.allocate( + KeypairEnum.KEYPAIR_LOCAL, + KeyBuildEnum.BUILD_KEYPAIR, + 256, + KeyClassEnum.ALG_EC_FP, + ) + clear_resp = target.clear(KeypairEnum.KEYPAIR_LOCAL) + assert clear_resp.success + + +def test_cleanup(target): + cleanup_resp = target.cleanup() + assert cleanup_resp.success + + +def test_info(target): + info_resp = target.info() + assert info_resp.success - def test_transform(self): - self.target.allocate( - KeypairEnum.KEYPAIR_LOCAL, - KeyBuildEnum.BUILD_KEYPAIR, - 256, - KeyClassEnum.ALG_EC_FP, - ) - self.target.set( - KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP - ) - self.target.generate(KeypairEnum.KEYPAIR_LOCAL) - export_privkey_resp1 = self.target.export( - KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PRIVATE, ParameterEnum.S - ) - privkey = int.from_bytes( - export_privkey_resp1.get_param(KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.S), - "big", - ) - transform_resp = self.target.transform( - KeypairEnum.KEYPAIR_LOCAL, - KeyEnum.PRIVATE, - ParameterEnum.S, - TransformationEnum.INCREMENT, - ) - self.assertTrue(transform_resp.success) - export_privkey_resp2 = self.target.export( - KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PRIVATE, ParameterEnum.S - ) - privkey_new = int.from_bytes( - export_privkey_resp2.get_param(KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.S), - "big", - ) - self.assertEqual(privkey + 1, privkey_new) - def test_ecdh(self): - self.target.allocate_ka(KeyAgreementEnum.ALG_EC_SVDP_DH) - self.target.allocate( - KeypairEnum.KEYPAIR_BOTH, - KeyBuildEnum.BUILD_KEYPAIR, - 256, - KeyClassEnum.ALG_EC_FP, - ) - self.target.set( - KeypairEnum.KEYPAIR_BOTH, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP - ) - self.target.generate(KeypairEnum.KEYPAIR_BOTH) - ecdh_resp = self.target.ecdh( - KeypairEnum.KEYPAIR_LOCAL, - KeypairEnum.KEYPAIR_REMOTE, - True, - TransformationEnum.NONE, - KeyAgreementEnum.ALG_EC_SVDP_DH, - ) - self.assertTrue(ecdh_resp.success) - export_public_resp = self.target.export( - KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PUBLIC, ParameterEnum.W - ) - pubkey_bytes = export_public_resp.get_param( - KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.W - ) - pubkey = self.secp256r1.curve.decode_point(pubkey_bytes) - export_privkey_resp = self.target.export( - KeypairEnum.KEYPAIR_REMOTE, KeyEnum.PRIVATE, ParameterEnum.S - ) - privkey = Mod( - int.from_bytes( - export_privkey_resp.get_param( - KeypairEnum.KEYPAIR_REMOTE, ParameterEnum.S - ), - "big", +def test_dry_run(target): + dry_run_resp = target.run_mode(RunModeEnum.MODE_DRY_RUN) + assert dry_run_resp.success + allocate_resp = target.allocate( + KeypairEnum.KEYPAIR_LOCAL, + KeyBuildEnum.BUILD_KEYPAIR, + 256, + KeyClassEnum.ALG_EC_FP, + ) + assert allocate_resp.success + dry_run_resp = target.run_mode(RunModeEnum.MODE_NORMAL) + assert dry_run_resp.success + + +def test_export(target, secp256r1_affine): + target.allocate( + KeypairEnum.KEYPAIR_LOCAL, + KeyBuildEnum.BUILD_KEYPAIR, + 256, + KeyClassEnum.ALG_EC_FP, + ) + target.set( + KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP + ) + target.generate(KeypairEnum.KEYPAIR_LOCAL) + export_public_resp = target.export( + KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PUBLIC, ParameterEnum.W + ) + assert export_public_resp.success + pubkey_bytes = export_public_resp.get_param( + KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.W + ) + pubkey = secp256r1_affine.curve.decode_point(pubkey_bytes) + export_privkey_resp = target.export( + KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PRIVATE, ParameterEnum.S + ) + assert export_privkey_resp.success + privkey = int.from_bytes( + export_privkey_resp.get_param(KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.S), + "big", + ) + assert pubkey == \ + secp256r1_affine.curve.affine_multiply(secp256r1_affine.generator, privkey) + + +def test_export_curve(target): + target.allocate( + KeypairEnum.KEYPAIR_LOCAL, + KeyBuildEnum.BUILD_KEYPAIR, + 256, + KeyClassEnum.ALG_EC_FP, + ) + target.set( + KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP + ) + export_resp = target.export( + KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PUBLIC, ParameterEnum.DOMAIN_FP + ) + assert export_resp.success + + +def test_transform(target): + target.allocate( + KeypairEnum.KEYPAIR_LOCAL, + KeyBuildEnum.BUILD_KEYPAIR, + 256, + KeyClassEnum.ALG_EC_FP, + ) + target.set( + KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP + ) + target.generate(KeypairEnum.KEYPAIR_LOCAL) + export_privkey_resp1 = target.export( + KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PRIVATE, ParameterEnum.S + ) + privkey = int.from_bytes( + export_privkey_resp1.get_param(KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.S), + "big", + ) + transform_resp = target.transform( + KeypairEnum.KEYPAIR_LOCAL, + KeyEnum.PRIVATE, + ParameterEnum.S, + TransformationEnum.INCREMENT, + ) + assert transform_resp.success + export_privkey_resp2 = target.export( + KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PRIVATE, ParameterEnum.S + ) + privkey_new = int.from_bytes( + export_privkey_resp2.get_param(KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.S), + "big", + ) + assert privkey + 1 == privkey_new + + +def test_ecdh(target, secp256r1_affine, secp256r1_projective): + target.allocate_ka(KeyAgreementEnum.ALG_EC_SVDP_DH) + target.allocate( + KeypairEnum.KEYPAIR_BOTH, + KeyBuildEnum.BUILD_KEYPAIR, + 256, + KeyClassEnum.ALG_EC_FP, + ) + target.set( + KeypairEnum.KEYPAIR_BOTH, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP + ) + target.generate(KeypairEnum.KEYPAIR_BOTH) + ecdh_resp = target.ecdh( + KeypairEnum.KEYPAIR_LOCAL, + KeypairEnum.KEYPAIR_REMOTE, + True, + TransformationEnum.NONE, + KeyAgreementEnum.ALG_EC_SVDP_DH, + ) + assert ecdh_resp.success + export_public_resp = target.export( + KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PUBLIC, ParameterEnum.W + ) + pubkey_bytes = export_public_resp.get_param( + KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.W + ) + pubkey = secp256r1_affine.curve.decode_point(pubkey_bytes) + export_privkey_resp = target.export( + KeypairEnum.KEYPAIR_REMOTE, KeyEnum.PRIVATE, ParameterEnum.S + ) + privkey = Mod( + int.from_bytes( + export_privkey_resp.get_param( + KeypairEnum.KEYPAIR_REMOTE, ParameterEnum.S ), - self.secp256r1.curve.prime, - ) - pubkey_projective = pubkey.to_model( - self.secp256r1_projective.curve.coordinate_model, self.secp256r1.curve - ) + "big", + ), + secp256r1_affine.curve.prime, + ) + pubkey_projective = pubkey.to_model( + secp256r1_projective.curve.coordinate_model, secp256r1_affine.curve + ) + + mult = LTRMultiplier( + secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"], + secp256r1_projective.curve.coordinate_model.formulas["dbl-2016-rcb"], + ) + ecdh = ECDH_SHA1(mult, secp256r1_projective, pubkey_projective, privkey) + expected = ecdh.perform() + assert ecdh_resp.secret == expected - mult = LTRMultiplier( - self.secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"], - self.secp256r1_projective.curve.coordinate_model.formulas["dbl-2016-rcb"], - ) - ecdh = ECDH_SHA1(mult, self.secp256r1_projective, pubkey_projective, privkey) - expected = ecdh.perform() - self.assertEqual(ecdh_resp.secret, expected) - def test_ecdh_raw(self): - self.target.allocate_ka(KeyAgreementEnum.ALG_EC_SVDP_DH) - self.target.allocate( - KeypairEnum.KEYPAIR_LOCAL, - KeyBuildEnum.BUILD_KEYPAIR, - 256, - KeyClassEnum.ALG_EC_FP, - ) - self.target.set( - KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP - ) - self.target.generate(KeypairEnum.KEYPAIR_LOCAL) - mult = LTRMultiplier( - self.secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"], - self.secp256r1_projective.curve.coordinate_model.formulas["dbl-2016-rcb"], - ) - keygen = KeyGeneration(copy(mult), self.secp256r1_projective) - _, pubkey_projective = keygen.generate() +def test_ecdh_raw(target, secp256r1_projective): + target.allocate_ka(KeyAgreementEnum.ALG_EC_SVDP_DH) + target.allocate( + KeypairEnum.KEYPAIR_LOCAL, + KeyBuildEnum.BUILD_KEYPAIR, + 256, + KeyClassEnum.ALG_EC_FP, + ) + target.set( + KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP + ) + target.generate(KeypairEnum.KEYPAIR_LOCAL) + mult = LTRMultiplier( + secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"], + secp256r1_projective.curve.coordinate_model.formulas["dbl-2016-rcb"], + ) + keygen = KeyGeneration(copy(mult), secp256r1_projective) + _, pubkey_projective = keygen.generate() - ecdh_resp = self.target.ecdh_direct( - KeypairEnum.KEYPAIR_LOCAL, - True, - TransformationEnum.NONE, - KeyAgreementEnum.ALG_EC_SVDP_DH, - bytes(pubkey_projective.to_affine()), - ) - self.assertTrue(ecdh_resp.success) - export_privkey_resp = self.target.export( - KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PRIVATE, ParameterEnum.S - ) - privkey = Mod( - int.from_bytes( - export_privkey_resp.get_param( - KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.S - ), - "big", + ecdh_resp = target.ecdh_direct( + KeypairEnum.KEYPAIR_LOCAL, + True, + TransformationEnum.NONE, + KeyAgreementEnum.ALG_EC_SVDP_DH, + bytes(pubkey_projective.to_affine()), + ) + assert ecdh_resp.success + export_privkey_resp = target.export( + KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PRIVATE, ParameterEnum.S + ) + privkey = Mod( + int.from_bytes( + export_privkey_resp.get_param( + KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.S ), - self.secp256r1.curve.prime, - ) + "big", + ), + secp256r1_projective.curve.prime, + ) - ecdh = ECDH_SHA1( - copy(mult), self.secp256r1_projective, pubkey_projective, privkey - ) - expected = ecdh.perform() - self.assertEqual(ecdh_resp.secret, expected) + ecdh = ECDH_SHA1( + copy(mult), secp256r1_projective, pubkey_projective, privkey + ) + expected = ecdh.perform() + assert ecdh_resp.secret == expected - def test_ecdsa(self): - self.target.allocate_sig(SignatureEnum.ALG_ECDSA_SHA) - self.target.allocate( - KeypairEnum.KEYPAIR_LOCAL, - KeyBuildEnum.BUILD_KEYPAIR, - 256, - KeyClassEnum.ALG_EC_FP, - ) - self.target.set( - KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP - ) - self.target.generate(KeypairEnum.KEYPAIR_LOCAL) - data = "Some text over here.".encode() - ecdsa_resp = self.target.ecdsa( - KeypairEnum.KEYPAIR_LOCAL, True, SignatureEnum.ALG_ECDSA_SHA, data - ) - self.assertTrue(ecdsa_resp.success) - export_public_resp = self.target.export( - KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PUBLIC, ParameterEnum.W - ) - pubkey_bytes = export_public_resp.get_param( - KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.W - ) - pubkey = self.secp256r1.curve.decode_point(pubkey_bytes) - pubkey_projective = pubkey.to_model( - self.secp256r1_projective.curve.coordinate_model, self.secp256r1.curve - ) - sig = SignatureResult.from_DER(ecdsa_resp.signature) - mult = LTRMultiplier( - self.secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"], - self.secp256r1_projective.curve.coordinate_model.formulas["dbl-2016-rcb"], - ) - ecdsa = ECDSA_SHA1( - copy(mult), - self.secp256r1_projective, - self.secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"], - pubkey_projective, - ) - self.assertTrue(ecdsa.verify_data(sig, data)) +def test_ecdsa(target, secp256r1_affine, secp256r1_projective): + target.allocate_sig(SignatureEnum.ALG_ECDSA_SHA) + target.allocate( + KeypairEnum.KEYPAIR_LOCAL, + KeyBuildEnum.BUILD_KEYPAIR, + 256, + KeyClassEnum.ALG_EC_FP, + ) + target.set( + KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP + ) + target.generate(KeypairEnum.KEYPAIR_LOCAL) + data = "Some text over here.".encode() + ecdsa_resp = target.ecdsa( + KeypairEnum.KEYPAIR_LOCAL, True, SignatureEnum.ALG_ECDSA_SHA, data + ) + assert ecdsa_resp.success + export_public_resp = target.export( + KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PUBLIC, ParameterEnum.W + ) + pubkey_bytes = export_public_resp.get_param( + KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.W + ) + pubkey = secp256r1_affine.curve.decode_point(pubkey_bytes) + pubkey_projective = pubkey.to_model( + secp256r1_projective.curve.coordinate_model, secp256r1_affine.curve + ) - def test_ecdsa_sign(self): - self.target.allocate_sig(SignatureEnum.ALG_ECDSA_SHA) - self.target.allocate( - KeypairEnum.KEYPAIR_LOCAL, - KeyBuildEnum.BUILD_KEYPAIR, - 256, - KeyClassEnum.ALG_EC_FP, - ) - self.target.set( - KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP - ) - self.target.generate(KeypairEnum.KEYPAIR_LOCAL) - data = "Some text over here.".encode() - ecdsa_resp = self.target.ecdsa_sign( - KeypairEnum.KEYPAIR_LOCAL, True, SignatureEnum.ALG_ECDSA_SHA, data - ) - self.assertTrue(ecdsa_resp.success) - export_public_resp = self.target.export( - KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PUBLIC, ParameterEnum.W - ) - pubkey_bytes = export_public_resp.get_param( - KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.W - ) - pubkey = self.secp256r1.curve.decode_point(pubkey_bytes) - pubkey_projective = pubkey.to_model( - self.secp256r1_projective.curve.coordinate_model, self.secp256r1.curve - ) + sig = SignatureResult.from_DER(ecdsa_resp.signature) + mult = LTRMultiplier( + secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"], + secp256r1_projective.curve.coordinate_model.formulas["dbl-2016-rcb"], + ) + ecdsa = ECDSA_SHA1( + copy(mult), + secp256r1_projective, + secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"], + pubkey_projective, + ) + assert ecdsa.verify_data(sig, data) - sig = SignatureResult.from_DER(ecdsa_resp.signature) - mult = LTRMultiplier( - self.secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"], - self.secp256r1_projective.curve.coordinate_model.formulas["dbl-2016-rcb"], - ) - ecdsa = ECDSA_SHA1( - copy(mult), - self.secp256r1_projective, - self.secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"], - pubkey_projective, - ) - self.assertTrue(ecdsa.verify_data(sig, data)) - def test_ecdsa_verify(self): - self.target.allocate_sig(SignatureEnum.ALG_ECDSA_SHA) - self.target.allocate( - KeypairEnum.KEYPAIR_LOCAL, - KeyBuildEnum.BUILD_KEYPAIR, - 256, - KeyClassEnum.ALG_EC_FP, - ) - self.target.set( - KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP - ) - mult = LTRMultiplier( - self.secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"], - self.secp256r1_projective.curve.coordinate_model.formulas["dbl-2016-rcb"], - ) - keygen = KeyGeneration(copy(mult), self.secp256r1_projective) - priv, pubkey_projective = keygen.generate() - self.target.set( - KeypairEnum.KEYPAIR_LOCAL, - CurveEnum.external, - ParameterEnum.W, - ECTesterTarget.encode_parameters( - ParameterEnum.W, pubkey_projective.to_affine() - ), - ) - ecdsa = ECDSA_SHA1( - copy(mult), - self.secp256r1_projective, - self.secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"], - pubkey_projective, - priv, - ) - data = "Some text over here.".encode() - sig = ecdsa.sign_data(data) +def test_ecdsa_sign(target, secp256r1_affine, secp256r1_projective): + target.allocate_sig(SignatureEnum.ALG_ECDSA_SHA) + target.allocate( + KeypairEnum.KEYPAIR_LOCAL, + KeyBuildEnum.BUILD_KEYPAIR, + 256, + KeyClassEnum.ALG_EC_FP, + ) + target.set( + KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP + ) + target.generate(KeypairEnum.KEYPAIR_LOCAL) + data = "Some text over here.".encode() + ecdsa_resp = target.ecdsa_sign( + KeypairEnum.KEYPAIR_LOCAL, True, SignatureEnum.ALG_ECDSA_SHA, data + ) + assert ecdsa_resp.success + export_public_resp = target.export( + KeypairEnum.KEYPAIR_LOCAL, KeyEnum.PUBLIC, ParameterEnum.W + ) + pubkey_bytes = export_public_resp.get_param( + KeypairEnum.KEYPAIR_LOCAL, ParameterEnum.W + ) + pubkey = secp256r1_affine.curve.decode_point(pubkey_bytes) + pubkey_projective = pubkey.to_model( + secp256r1_projective.curve.coordinate_model, secp256r1_affine.curve + ) + + sig = SignatureResult.from_DER(ecdsa_resp.signature) + mult = LTRMultiplier( + secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"], + secp256r1_projective.curve.coordinate_model.formulas["dbl-2016-rcb"], + ) + ecdsa = ECDSA_SHA1( + copy(mult), + secp256r1_projective, + secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"], + pubkey_projective, + ) + assert ecdsa.verify_data(sig, data) + + +def test_ecdsa_verify(target, secp256r1_projective): + target.allocate_sig(SignatureEnum.ALG_ECDSA_SHA) + target.allocate( + KeypairEnum.KEYPAIR_LOCAL, + KeyBuildEnum.BUILD_KEYPAIR, + 256, + KeyClassEnum.ALG_EC_FP, + ) + target.set( + KeypairEnum.KEYPAIR_LOCAL, CurveEnum.secp256r1, ParameterEnum.DOMAIN_FP + ) + mult = LTRMultiplier( + secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"], + secp256r1_projective.curve.coordinate_model.formulas["dbl-2016-rcb"], + ) + keygen = KeyGeneration(copy(mult), secp256r1_projective) + priv, pubkey_projective = keygen.generate() + target.set( + KeypairEnum.KEYPAIR_LOCAL, + CurveEnum.external, + ParameterEnum.W, + ECTesterTarget.encode_parameters( + ParameterEnum.W, pubkey_projective.to_affine() + ), + ) + ecdsa = ECDSA_SHA1( + copy(mult), + secp256r1_projective, + secp256r1_projective.curve.coordinate_model.formulas["add-2016-rcb"], + pubkey_projective, + priv, + ) + data = "Some text over here.".encode() + sig = ecdsa.sign_data(data) - ecdsa_resp = self.target.ecdsa_verify( - KeypairEnum.KEYPAIR_LOCAL, SignatureEnum.ALG_ECDSA_SHA, sig.to_DER(), data - ) - self.assertTrue(ecdsa_resp.success) + ecdsa_resp = target.ecdsa_verify( + KeypairEnum.KEYPAIR_LOCAL, SignatureEnum.ALG_ECDSA_SHA, sig.to_DER(), data + ) + assert ecdsa_resp.success diff --git a/test/sca/test_test.py b/test/sca/test_test.py index 7b4f346..256e77d 100644 --- a/test/sca/test_test.py +++ b/test/sca/test_test.py @@ -1,43 +1,41 @@ -from unittest import TestCase - +from collections import namedtuple import numpy as np +import pytest from pyecsca.sca import Trace, welch_ttest, student_ttest, ks_test -class TTestTests(TestCase): - def setUp(self): - self.a = Trace(np.array([20, 80], dtype=np.dtype("i1"))) - self.b = Trace(np.array([30, 42], dtype=np.dtype("i1"))) - self.c = Trace(np.array([78, 56], dtype=np.dtype("i1"))) - self.d = Trace(np.array([98, 36], dtype=np.dtype("i1"))) +@pytest.fixture() +def data(): + Data = namedtuple("Data", ["a", "b", "c", "d"]) + return Data(a=Trace(np.array([20, 80], dtype=np.dtype("i1"))), + b=Trace(np.array([30, 42], dtype=np.dtype("i1"))), + c=Trace(np.array([78, 56], dtype=np.dtype("i1"))), + d=Trace(np.array([98, 36], dtype=np.dtype("i1")))) + + +def test_welch_ttest(data): + assert welch_ttest([data.a, data.b], [data.c, data.d]) is not None + a = Trace( + np.array([19.8, 20.4, 19.6, 17.8, 18.5, 18.9, 18.3, 18.9, 19.5, 22.0]) + ) + b = Trace( + np.array([28.2, 26.6, 20.1, 23.3, 25.2, 22.1, 17.7, 27.6, 20.6, 13.7]) + ) + c = Trace( + np.array([20.2, 21.6, 27.1, 13.3, 24.2, 20.1, 11.7, 25.6, 26.6, 21.4]) + ) - def test_welch_ttest(self): - self.assertIsNotNone(welch_ttest([self.a, self.b], [self.c, self.d])) - a = Trace( - np.array([19.8, 20.4, 19.6, 17.8, 18.5, 18.9, 18.3, 18.9, 19.5, 22.0]) - ) - b = Trace( - np.array([28.2, 26.6, 20.1, 23.3, 25.2, 22.1, 17.7, 27.6, 20.6, 13.7]) - ) - c = Trace( - np.array([20.2, 21.6, 27.1, 13.3, 24.2, 20.1, 11.7, 25.6, 26.6, 21.4]) - ) + result = welch_ttest([a, b], [b, c], dof=True, p_value=True) + assert result is not None - result = welch_ttest([a, b], [b, c], dof=True, p_value=True) - self.assertIsNotNone(result) - def test_students_ttest(self): - self.assertIsNone(student_ttest([], [])) - self.assertIsNotNone(student_ttest([self.a, self.b], [self.c, self.d])) +def test_students_ttest(data): + assert student_ttest([], []) is None + assert student_ttest([data.a, data.b], [data.c, data.d]) is not None -class KolmogorovSmirnovTests(TestCase): - def test_ks_test(self): - self.assertIsNone(ks_test([], [])) +def test_ks_test(data): + assert ks_test([], []) is None - a = Trace(np.array([20, 80], dtype=np.dtype("i1"))) - b = Trace(np.array([30, 42], dtype=np.dtype("i1"))) - c = Trace(np.array([78, 56], dtype=np.dtype("i1"))) - d = Trace(np.array([98, 36], dtype=np.dtype("i1"))) - self.assertIsNotNone(ks_test([a, b], [c, d])) + assert ks_test([data.a, data.b], [data.c, data.d]) is not None diff --git a/test/sca/test_trace.py b/test/sca/test_trace.py index 93baa89..98818d3 100644 --- a/test/sca/test_trace.py +++ b/test/sca/test_trace.py @@ -1,11 +1,9 @@ -from unittest import TestCase import numpy as np from pyecsca.sca import Trace -class TraceTests(TestCase): - def test_basic(self): - trace = Trace(np.array([10, 15, 24], dtype=np.dtype("i1"))) - self.assertIsNotNone(trace) - self.assertIn("Trace", str(trace)) - self.assertIsNone(trace.trace_set) +def test_basic(): + trace = Trace(np.array([10, 15, 24], dtype=np.dtype("i1"))) + assert trace is not None + assert "Trace" in str(trace) + assert trace.trace_set is None diff --git a/test/sca/test_traceset.py b/test/sca/test_traceset.py index e79e5d2..a338a9d 100644 --- a/test/sca/test_traceset.py +++ b/test/sca/test_traceset.py @@ -1,10 +1,13 @@ import os.path import shutil import tempfile -from unittest import TestCase + +import pytest +from importlib_resources import files, as_file import numpy as np +import test.data.sca from pyecsca.sca import ( TraceSet, InspectorTraceSet, @@ -14,115 +17,126 @@ from pyecsca.sca import ( Trace, ) -EXAMPLE_TRACES = [ - Trace(np.array([20, 40, 50, 50, 10], dtype=np.dtype("i1")), {"something": 5}), - Trace(np.array([1, 2, 3, 4, 5], dtype=np.dtype("i1"))), - Trace(np.array([6, 7, 8, 9, 10], dtype=np.dtype("i1"))), -] -EXAMPLE_KWARGS = {"num_traces": 3, "thingy": "abc"} + +@pytest.fixture() +def example_traces(): + return [ + Trace(np.array([20, 40, 50, 50, 10], dtype=np.dtype("i1")), {"something": 5}), + Trace(np.array([1, 2, 3, 4, 5], dtype=np.dtype("i1"))), + Trace(np.array([6, 7, 8, 9, 10], dtype=np.dtype("i1"))), + ] + + +@pytest.fixture() +def example_kwargs(): + return {"num_traces": 3, "thingy": "abc"} -class TraceSetTests(TestCase): - def test_create(self): - self.assertIsNotNone(TraceSet()) - self.assertIsNotNone(InspectorTraceSet()) - self.assertIsNotNone(ChipWhispererTraceSet()) - self.assertIsNotNone(PickleTraceSet()) - self.assertIsNotNone(HDF5TraceSet()) +def test_create(): + assert TraceSet() is not None + assert InspectorTraceSet() is not None + assert ChipWhispererTraceSet() is not None + assert PickleTraceSet() is not None + assert HDF5TraceSet() is not None -class InspectorTraceSetTests(TestCase): - def test_load_fname(self): - result = InspectorTraceSet.read("test/data/example.trs") - self.assertIsNotNone(result) - self.assertEqual(result.global_title, "Example trace set") - self.assertEqual(len(result), 10) - self.assertEqual(len(list(result)), 10) - self.assertIn("InspectorTraceSet", str(result)) - self.assertIs(result[0].trace_set, result) - self.assertEqual(result.sampling_frequency, 12500000) +def test_trs_load_fname(): + with as_file(files(test.data.sca).joinpath("example.trs")) as path: + result = InspectorTraceSet.read(path) + assert result is not None + assert result.global_title == "Example trace set" + assert len(result) == 10 + assert len(list(result)) == 10 + assert "InspectorTraceSet" in str(result) + assert result[0].trace_set is result + assert result.sampling_frequency == 12500000 - def test_load_file(self): - with open("test/data/example.trs", "rb") as f: - self.assertIsNotNone(InspectorTraceSet.read(f)) - def test_load_bytes(self): - with open("test/data/example.trs", "rb") as f: - self.assertIsNotNone(InspectorTraceSet.read(f.read())) +def test_trs_load_file(): + with files(test.data.sca).joinpath("example.trs").open("rb") as f: + assert InspectorTraceSet.read(f) is not None - def test_save(self): - trace_set = InspectorTraceSet.read("test/data/example.trs") + +def test_trs_load_bytes(): + with files(test.data.sca).joinpath("example.trs").open("rb") as f: + assert InspectorTraceSet.read(f.read()) is not None + + +def test_trs_save(): + with as_file(files(test.data.sca).joinpath("example.trs")) as path: + trace_set = InspectorTraceSet.read(path) with tempfile.TemporaryDirectory() as dirname: path = os.path.join(dirname, "out.trs") trace_set.write(path) - self.assertTrue(os.path.exists(path)) - self.assertIsNotNone(InspectorTraceSet.read(path)) + assert os.path.exists(path) + assert InspectorTraceSet.read(path) is not None -class ChipWhispererTraceSetTests(TestCase): - def test_load_fname(self): - result = ChipWhispererTraceSet.read("test/data/config_chipwhisperer_.cfg") - self.assertIsNotNone(result) - self.assertEqual(len(result), 2) +def test_cw_load_fname(): + with as_file(files(test.data.sca).joinpath("config_chipwhisperer_.cfg")) as path: + # This will not work if the test package is not on the file system directly. + result = ChipWhispererTraceSet.read(path) + assert result is not None + assert len(result) == 2 -class PickleTraceSetTests(TestCase): - def test_load_fname(self): - result = PickleTraceSet.read("test/data/test.pickle") - self.assertIsNotNone(result) +def test_pickle_load_fname(): + with as_file(files(test.data.sca).joinpath("test.pickle")) as path: + result = PickleTraceSet.read(path) + assert result is not None - def test_load_file(self): - with open("test/data/test.pickle", "rb") as f: - self.assertIsNotNone(PickleTraceSet.read(f)) - def test_save(self): - trace_set = PickleTraceSet(*EXAMPLE_TRACES, **EXAMPLE_KWARGS) - with tempfile.TemporaryDirectory() as dirname: - path = os.path.join(dirname, "out.pickle") - trace_set.write(path) - self.assertTrue(os.path.exists(path)) - self.assertIsNotNone(PickleTraceSet.read(path)) +def test_pickle_load_file(): + with files(test.data.sca).joinpath("test.pickle").open("rb") as f: + assert PickleTraceSet.read(f) is not None -class HDF5TraceSetTests(TestCase): - def test_load_fname(self): - result = HDF5TraceSet.read("test/data/test.h5") - self.assertIsNotNone(result) +def test_pickle_save(example_traces, example_kwargs): + trace_set = PickleTraceSet(*example_traces, **example_kwargs) + with tempfile.TemporaryDirectory() as dirname: + path = os.path.join(dirname, "out.pickle") + trace_set.write(path) + assert os.path.exists(path) + assert PickleTraceSet.read(path) is not None - def test_load_file(self): - with open("test/data/test.h5", "rb") as f: - self.assertIsNotNone(HDF5TraceSet.read(f)) - def test_inplace(self): - with tempfile.TemporaryDirectory() as dirname: - path = os.path.join(dirname, "test.h5") - shutil.copy("test/data/test.h5", path) - trace_set = HDF5TraceSet.inplace(path) - self.assertIsNotNone(trace_set) - test_trace = Trace( - np.array([6, 7], dtype=np.dtype("i1")), meta={"thing": "ring"} - ) - other_trace = Trace( - np.array([15, 7], dtype=np.dtype("i1")), meta={"a": "b"} - ) - trace_set.append(test_trace) - self.assertEqual(len(trace_set), 4) - trace_set.append(other_trace) - trace_set.remove(other_trace) - self.assertEqual(len(trace_set), 4) - trace_set.save() - trace_set.close() +def test_h5_load_fname(): + with as_file(files(test.data.sca).joinpath("test.h5")) as path: + result = HDF5TraceSet.read(path) + assert result is not None - test_set = HDF5TraceSet.read(path) - self.assertEqual(test_set.get(3), test_set[3]) - self.assertTrue(np.array_equal(test_set[3].samples, test_trace.samples)) - self.assertEqual(test_set[3].meta["thing"], test_trace.meta["thing"]) - self.assertEqual(test_set[3], test_trace) - def test_save(self): - trace_set = HDF5TraceSet(*EXAMPLE_TRACES, **EXAMPLE_KWARGS) - with tempfile.TemporaryDirectory() as dirname: - path = os.path.join(dirname, "out.h5") - trace_set.write(path) - self.assertTrue(os.path.exists(path)) - self.assertIsNotNone(HDF5TraceSet.read(path)) +def test_h5_load_file(): + with files(test.data.sca).joinpath("test.h5").open("rb") as f: + assert HDF5TraceSet.read(f) is not None + + +def test_h5_inplace(): + with tempfile.TemporaryDirectory() as dirname, as_file(files(test.data.sca).joinpath("test.h5")) as orig_path: + path = os.path.join(dirname, "test.h5") + shutil.copy(orig_path, path) + trace_set = HDF5TraceSet.inplace(path) + assert trace_set is not None + test_trace = Trace(np.array([6, 7], dtype=np.dtype("i1")), meta={"thing": "ring"}) + other_trace = Trace(np.array([15, 7], dtype=np.dtype("i1")), meta={"a": "b"}) + trace_set.append(test_trace) + assert len(trace_set) == 4 + trace_set.append(other_trace) + trace_set.remove(other_trace) + assert len(trace_set) == 4 + trace_set.save() + trace_set.close() + test_set = HDF5TraceSet.read(path) + assert test_set.get(3) == test_set[3] + assert np.array_equal(test_set[3].samples, test_trace.samples) + assert test_set[3].meta["thing"] == test_trace.meta["thing"] + assert test_set[3] == test_trace + + +def test_h5_save(example_traces, example_kwargs): + trace_set = HDF5TraceSet(*example_traces, **example_kwargs) + with tempfile.TemporaryDirectory() as dirname: + path = os.path.join(dirname, "out.h5") + trace_set.write(path) + assert os.path.exists(path) + assert HDF5TraceSet.read(path) is not None diff --git a/test/sca/test_zvp.py b/test/sca/test_zvp.py new file mode 100644 index 0000000..3b6b046 --- /dev/null +++ b/test/sca/test_zvp.py @@ -0,0 +1,13 @@ +from pyecsca.sca.re.zvp import unroll_formula + + +def test_unroll(secp128r1): + add = secp128r1.curve.coordinate_model.formulas["add-2007-bl"] + dbl = secp128r1.curve.coordinate_model.formulas["dbl-2007-bl"] + neg = secp128r1.curve.coordinate_model.formulas["neg"] + results = unroll_formula(add, 11) + assert results is not None + results = unroll_formula(dbl, 11) + assert results is not None + results = unroll_formula(neg, 11) + assert results is not None diff --git a/test/sca/utils.py b/test/sca/utils.py deleted file mode 100644 index b00015b..0000000 --- a/test/sca/utils.py +++ /dev/null @@ -1,52 +0,0 @@ -from os import mkdir, getenv, getcwd -from os.path import join, exists, split -from typing import Dict -from unittest import TestCase - -import matplotlib.pyplot as plt - -from pyecsca.sca import Trace - -force_plot = True - - -def slow(func): - func.slow = 1 - return func - - -def disabled(func): - func.disabled = 1 - return func - - -cases: Dict[str, int] = {} - - -class Plottable(TestCase): - def get_dir(self): - if split(getcwd())[1] == "test": - directory = "plots" - else: - directory = join("test", "plots") - if not exists(directory): - mkdir(directory) - return directory - - def get_fname(self): - directory = self.get_dir() - case_id = cases.setdefault(self.id(), 0) + 1 - cases[self.id()] = case_id - return join(directory, self.id() + str(case_id)) - - def plot(self, *traces: Trace, **kwtraces: Trace): - if not force_plot and getenv("PYECSCA_TEST_PLOTS") is None: - return - fig = plt.figure() - ax = fig.add_subplot(111) - for i, trace in enumerate(traces): - ax.plot(trace.samples, label=str(i)) - for name, trace in kwtraces.items(): - ax.plot(trace.samples, label=name) - ax.legend(loc="best") - plt.savefig(self.get_fname() + ".png") diff --git a/unittest.cfg b/unittest.cfg deleted file mode 100644 index 7c90619..0000000 --- a/unittest.cfg +++ /dev/null @@ -1,3 +0,0 @@ -[unittest] -plugins = nose2.plugins.attrib - nose2.plugins.doctests |
