From 9ed79b918009580a04649c7acdce63247d2314ed Mon Sep 17 00:00:00 2001 From: J08nY Date: Tue, 3 Mar 2020 13:32:14 +0100 Subject: Add support for getting neutral points in coordinate systems. --- Makefile | 2 +- pyecsca/ec/coordinates.py | 10 +++++++++- pyecsca/ec/curves.py | 19 +++++++++++++++++-- pyecsca/ec/efd/shortw/jacobian-0/variables | 3 +++ pyecsca/ec/efd/shortw/jacobian-3/variables | 3 +++ pyecsca/ec/efd/shortw/jacobian/variables | 3 +++ pyecsca/ec/efd/shortw/projective-1/variables | 3 +++ pyecsca/ec/efd/shortw/projective-3/variables | 3 +++ pyecsca/ec/efd/shortw/projective/variables | 3 +++ pyecsca/sca/target/binary.py | 12 ++++++++---- pyecsca/sca/target/chipwhisperer.py | 1 - test/data/target.py | 17 +++++++++++++++++ test/ec/test_curves.py | 5 +++++ test/sca/test_target.py | 20 ++++++++++++++++++++ 14 files changed, 95 insertions(+), 9 deletions(-) create mode 100644 test/data/target.py create mode 100644 test/sca/test_target.py diff --git a/Makefile b/Makefile index 0f52507..0e43a25 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ ec.test_params ec.test_key_agreement ec.test_key_generation ec.test_mod ec.test_ ec.test_mult ec.test_naf ec.test_op ec.test_point ec.test_signature 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_test sca.test_trace sca.test_traceset +sca.test_sampling sca.test_target sca.test_test sca.test_trace sca.test_traceset TESTS = ${EC_TESTS} ${SCA_TESTS} diff --git a/pyecsca/ec/coordinates.py b/pyecsca/ec/coordinates.py index 1838659..f8642cf 100644 --- a/pyecsca/ec/coordinates.py +++ b/pyecsca/ec/coordinates.py @@ -21,6 +21,7 @@ class CoordinateModel(object): satisfying: List[Module] parameters: List[str] assumptions: List[Module] + neutral: List[Module] formulas: MutableMapping[str, Formula] def __repr__(self): @@ -38,6 +39,7 @@ class AffineCoordinateModel(CoordinateModel): self.satisfying = [] self.parameters = [] self.assumptions = [] + self.neutral = [] self.formulas = {} def __eq__(self, other): @@ -55,6 +57,7 @@ class EFDCoordinateModel(CoordinateModel): self.satisfying = [] self.parameters = [] self.assumptions = [] + self.neutral = [] self.formulas = {} for fname in resource_listdir(__name__, dir_path): file_path = join(dir_path, fname) @@ -88,12 +91,17 @@ class EFDCoordinateModel(CoordinateModel): self.full_name = line[5:] elif line.startswith("variable"): self.variables.append(line[9:]) + elif line.startswith("neutral"): + try: + code = parse(line[8:].replace("^", "**"), mode="exec") + self.neutral.append(code) + except SyntaxError: + pass elif line.startswith("satisfying"): try: code = parse(line[11:].replace("^", "**"), mode="exec") self.satisfying.append(code) except SyntaxError: - # code = parse(line[11:].replace("=", "==").replace("^", "**"), mode="eval") pass elif line.startswith("parameter"): self.parameters.append(line[10:]) diff --git a/pyecsca/ec/curves.py b/pyecsca/ec/curves.py index 9240be2..bc8329f 100644 --- a/pyecsca/ec/curves.py +++ b/pyecsca/ec/curves.py @@ -14,7 +14,7 @@ from .point import Point, InfinityPoint @public -def get_params(category: str, name: str, coords: str) -> DomainParameters: +def get_params(category: str, name: str, coords: str, infty: bool = True) -> DomainParameters: """ Retrieve a curve from a set of stored parameters. Uses the std-curves database at https://github.com/J08nY/std-curves. @@ -22,6 +22,8 @@ def get_params(category: str, name: str, coords: str) -> DomainParameters: :param category: The category of the curve. :param name: The name of the curve. :param coords: The name of the coordinate system to use. + :param infty: Whether to use the special :py:class:InfinityPoint (`True`) or try to use the + point at infinity of the coordinate system. :return: The curve. """ listing = resource_listdir(__name__, "std") @@ -68,7 +70,20 @@ def get_params(category: str, name: str, coords: str) -> DomainParameters: for param, value in locals.items(): if params[param] != value: raise ValueError(f"Coordinate model {coord_model} has an unsatisifed assumption on the {param} parameter (= {value}).") - elliptic_curve = EllipticCurve(model, coord_model, field, InfinityPoint(coord_model), params) + if infty: + infinity = InfinityPoint(coord_model) + else: + locals = {**params} + for line in coord_model.neutral: + compiled = compile(line, "", mode="exec") + exec(compiled, None, locals) + infinity_coords = {} + for coordinate in coord_model.variables: + if coordinate not in locals: + raise ValueError(f"Coordinate model {coord_model} requires infty option.") + infinity_coords[coordinate] = Mod(locals[coordinate], field) + infinity = Point(coord_model, **infinity_coords) + elliptic_curve = EllipticCurve(model, coord_model, field, infinity, params) affine = Point(AffineCoordinateModel(model), x=Mod(int(curve["generator"]["x"], 16), field), y=Mod(int(curve["generator"]["y"], 16), field)) generator = Point.from_affine(coord_model, affine) diff --git a/pyecsca/ec/efd/shortw/jacobian-0/variables b/pyecsca/ec/efd/shortw/jacobian-0/variables index 5e84b72..6ea9908 100644 --- a/pyecsca/ec/efd/shortw/jacobian-0/variables +++ b/pyecsca/ec/efd/shortw/jacobian-0/variables @@ -3,5 +3,8 @@ assume a = 0 variable X variable Y variable Z +neutral X = 1 +neutral Y = 1 +neutral Z = 0 satisfying x = X/Z^2 satisfying y = Y/Z^3 diff --git a/pyecsca/ec/efd/shortw/jacobian-3/variables b/pyecsca/ec/efd/shortw/jacobian-3/variables index cddb049..a4d2b49 100644 --- a/pyecsca/ec/efd/shortw/jacobian-3/variables +++ b/pyecsca/ec/efd/shortw/jacobian-3/variables @@ -3,5 +3,8 @@ assume a = -3 variable X variable Y variable Z +neutral X = 1 +neutral Y = 1 +neutral Z = 0 satisfying x = X/Z^2 satisfying y = Y/Z^3 diff --git a/pyecsca/ec/efd/shortw/jacobian/variables b/pyecsca/ec/efd/shortw/jacobian/variables index 7258c13..ddfbff6 100644 --- a/pyecsca/ec/efd/shortw/jacobian/variables +++ b/pyecsca/ec/efd/shortw/jacobian/variables @@ -2,5 +2,8 @@ name Jacobian coordinates variable X variable Y variable Z +neutral X = 1 +neutral Y = 1 +neutral Z = 0 satisfying x = X/Z^2 satisfying y = Y/Z^3 diff --git a/pyecsca/ec/efd/shortw/projective-1/variables b/pyecsca/ec/efd/shortw/projective-1/variables index 49b8f7a..f807365 100644 --- a/pyecsca/ec/efd/shortw/projective-1/variables +++ b/pyecsca/ec/efd/shortw/projective-1/variables @@ -3,5 +3,8 @@ assume a = -1 variable X variable Y variable Z +neutral X = 0 +neutral Y = 1 +neutral Z = 0 satisfying x = X/Z satisfying y = Y/Z diff --git a/pyecsca/ec/efd/shortw/projective-3/variables b/pyecsca/ec/efd/shortw/projective-3/variables index 1de70b5..2458576 100644 --- a/pyecsca/ec/efd/shortw/projective-3/variables +++ b/pyecsca/ec/efd/shortw/projective-3/variables @@ -3,5 +3,8 @@ assume a = -3 variable X variable Y variable Z +neutral X = 0 +neutral Y = 1 +neutral Z = 0 satisfying x = X/Z satisfying y = Y/Z diff --git a/pyecsca/ec/efd/shortw/projective/variables b/pyecsca/ec/efd/shortw/projective/variables index 9c6045b..4ba1c7b 100644 --- a/pyecsca/ec/efd/shortw/projective/variables +++ b/pyecsca/ec/efd/shortw/projective/variables @@ -2,5 +2,8 @@ name projective coordinates variable X variable Y variable Z +neutral X = 0 +neutral Y = 1 +neutral Z = 0 satisfying x = X/Z satisfying y = Y/Z diff --git a/pyecsca/sca/target/binary.py b/pyecsca/sca/target/binary.py index 62d1102..9a469b6 100644 --- a/pyecsca/sca/target/binary.py +++ b/pyecsca/sca/target/binary.py @@ -1,6 +1,6 @@ import subprocess from subprocess import Popen -from typing import Optional +from typing import Optional, Union, List from public import public @@ -9,17 +9,21 @@ from .serial import SerialTarget @public class BinaryTarget(SerialTarget): - binary: str + binary: List[str] process: Optional[Popen] debug_output: bool - def __init__(self, binary: str, debug_output: bool = False, **kwargs): + def __init__(self, binary: Union[str, List[str]], debug_output: bool = False, **kwargs): super().__init__(**kwargs) + if not isinstance(binary, (str, list)): + raise TypeError + if isinstance(binary, str): + binary = [binary] self.binary = binary self.debug_output = debug_output def connect(self): - self.process = Popen([self.binary], stdin=subprocess.PIPE, stdout=subprocess.PIPE, + self.process = Popen(self.binary, stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True, bufsize=1) def write(self, data: bytes): diff --git a/pyecsca/sca/target/chipwhisperer.py b/pyecsca/sca/target/chipwhisperer.py index 0528618..1dcdbe3 100644 --- a/pyecsca/sca/target/chipwhisperer.py +++ b/pyecsca/sca/target/chipwhisperer.py @@ -1,4 +1,3 @@ -from binascii import unhexlify from typing import Optional from time import sleep diff --git a/test/data/target.py b/test/data/target.py new file mode 100644 index 0000000..1583597 --- /dev/null +++ b/test/data/target.py @@ -0,0 +1,17 @@ +from sys import stdout + +if __name__ == "__main__": + + while True: + line = input() + char = line[0] + content = line[1:] + if char == "d": + for c in "r01020304": + stdout.write(c) + stdout.write("\n") + for c in "z00": + stdout.write(c) + stdout.write("\n") + elif char == "x": + break \ No newline at end of file diff --git a/test/ec/test_curves.py b/test/ec/test_curves.py index 11861c4..fba932a 100644 --- a/test/ec/test_curves.py +++ b/test/ec/test_curves.py @@ -37,6 +37,11 @@ class CurvesTests(TestCase): get_params("secg", "secp128r1", "projective-1") self.assertIsNotNone(get_params("secg", "secp128r1", "projective-3")) + def test_infty(self): + with self.assertRaises(ValueError): + get_params("secg", "secp128r1", "modified", False) + self.assertIsNotNone(get_params("secg", "secp128r1", "projective", False)) + def test_no_binary(self): with self.assertRaises(ValueError): get_params("secg", "sect163r1", "something") \ No newline at end of file diff --git a/test/sca/test_target.py b/test/sca/test_target.py new file mode 100644 index 0000000..25d506a --- /dev/null +++ b/test/sca/test_target.py @@ -0,0 +1,20 @@ +from unittest import TestCase +from os.path import realpath, dirname, join + + +from pyecsca.sca.target import BinaryTarget, SimpleSerialTarget, SimpleSerialMessage + +class TestTarget(SimpleSerialTarget, BinaryTarget): + pass + +class BinaryTargetTests(TestCase): + + def test_basic_target(self): + target_path = join(dirname(realpath(__file__)), "..", "data", "target.py") + 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") + target.disconnect() \ No newline at end of file -- cgit v1.2.3-70-g09d2