diff options
| author | J08nY | 2019-11-28 19:35:33 +0100 |
|---|---|---|
| committer | J08nY | 2019-11-28 19:35:33 +0100 |
| commit | 878d95c4e4dadf882a205316a07bc0642f773256 (patch) | |
| tree | e539512c2a5386d714b6c29735b6bd17d30b2ca6 /pyecsca/codegen | |
| parent | a17c64b710688ac697427544eaaab4aae6188c3c (diff) | |
| download | pyecsca-codegen-878d95c4e4dadf882a205316a07bc0642f773256.tar.gz pyecsca-codegen-878d95c4e4dadf882a205316a07bc0642f773256.tar.zst pyecsca-codegen-878d95c4e4dadf882a205316a07bc0642f773256.zip | |
Split to builder and client.
Diffstat (limited to 'pyecsca/codegen')
| -rw-r--r-- | pyecsca/codegen/__init__.py | 236 | ||||
| -rw-r--r-- | pyecsca/codegen/bn/bn.c | 6 | ||||
| -rw-r--r-- | pyecsca/codegen/builder.py | 447 | ||||
| -rw-r--r-- | pyecsca/codegen/client.py | 91 | ||||
| -rw-r--r-- | pyecsca/codegen/fat.h | 7 | ||||
| -rw-r--r-- | pyecsca/codegen/templates/main.c | 4 |
6 files changed, 549 insertions, 242 deletions
diff --git a/pyecsca/codegen/__init__.py b/pyecsca/codegen/__init__.py index 06c85c2..e69de29 100644 --- a/pyecsca/codegen/__init__.py +++ b/pyecsca/codegen/__init__.py @@ -1,236 +0,0 @@ -import os -import tempfile -from ast import operator, Add, Sub, Mult, Div, Pow -from enum import Enum -from os import path -from typing import List, Set, Mapping - -from jinja2 import Environment, PackageLoader -from pkg_resources import resource_filename -from pyecsca.ec.coordinates import CoordinateModel -from pyecsca.ec.formula import (Formula, AdditionFormula, DoublingFormula, TriplingFormula, - NegationFormula, ScalingFormula, DifferentialAdditionFormula, - LadderFormula) -from pyecsca.ec.model import CurveModel, ShortWeierstrassModel -from pyecsca.ec.op import CodeOp - -env = Environment( - loader=PackageLoader("pyecsca.codegen") -) - - -def render_op(op: operator, result: str, left: str, right: str, mod: str): - if isinstance(op, Add): - return "bn_mod_add(&{}, &{}, &{}, &{});".format(left, right, mod, result) - elif isinstance(op, Sub): - return "bn_mod_sub(&{}, &{}, &{}, &{});".format(left, right, mod, result) - elif isinstance(op, Mult): - return "bn_mod_mul(&{}, &{}, &{}, &{});".format(left, right, mod, result) - elif isinstance(op, Div): - return "bn_mod_div(&{}, &{}, &{}, &{});".format(left, right, mod, result) - elif isinstance(op, Pow) and right == 2: - return "bn_mod_sqr(&{}, &{}, &{});".format(left, mod, result) - elif isinstance(op, Pow): - return "bn_mod_pow(&{}, &{}, &{}, &{});".format(left, right, mod, result) - else: - print(op, result, left, right, mod) - - -env.globals["render_op"] = render_op - - -class EnumDefine(Enum): - def __str__(self): - return self.value - - -class Platform(EnumDefine): - HOST = "HOST" - XMEGA = "CW308_XMEGA" - STM32F0 = "CW308_STM32F0" - STM32F3 = "CW308_STM32F3" - - -class HashType(EnumDefine): - NONE = "HASH_NONE" - SHA1 = "HASH_SHA1" - SHA224 = "HASH_SHA224" - SHA256 = "HASH_SHA256" - SHA384 = "HASH_SHA384" - SHA512 = "HASH_SHA512" - - -class MultAlgo(EnumDefine): - NONE = "MULT_NONE" - DOUBLE_AND_ADD = "MULT_DOUBLE_AND_ADD" - - -class RandomMod(EnumDefine): - SAMPLE = "MOD_RAND_SAMPLE" - REDUCE = "MOD_RAND_REDUCE" - - -def render_defs(model: CurveModel, coords: CoordinateModel): - return env.get_template("defs.h").render(params=model.parameter_names, - variables=coords.variables) - - -def render_curve_impl(model: CurveModel): - return env.get_template("curve.c").render(params=model.parameter_names) - - -def transform_ops(ops: List[CodeOp], parameters: List[str], outputs: Set[str], - renames: Mapping[str, str] = None): - def rename(name: str): - if renames is not None and name not in outputs: - return renames.get(name, name) - return name - - allocations = [] - initializations = {} - const_mapping = {} - operations = [] - frees = [] - for op in ops: - if op.result not in allocations: - allocations.append(op.result) - frees.append(op.result) - for param in op.parameters: - if param not in allocations and param not in parameters: - raise ValueError("Should be allocated or parameter: {}".format(param)) - for const in op.constants: - name = "c" + str(const) - if name not in allocations: - allocations.append(name) - initializations[name] = const - const_mapping[const] = name - frees.append(name) - operations.append((op.operator, op.result, rename(op.left), rename(op.right))) - mapped = [] - for op in operations: - o2 = op[2] - if o2 in const_mapping: - o2 = const_mapping[o2] - o3 = op[3] - if o3 in const_mapping and not (isinstance(op[0], Pow) and o3 == 2): - o3 = const_mapping[o3] - mapped.append((op[0], op[1], o2, o3)) - returns = {} - if renames: - for r_from, r_to in renames.items(): - if r_from in outputs: - returns[r_from] = r_to - - return dict(allocations=allocations, - initializations=initializations, - const_mapping=const_mapping, operations=mapped, - frees=frees, returns=returns) - - -def render_ops(ops: List[CodeOp], parameters: List[str], outputs: Set[str], - renames: Mapping[str, str] = None): - namespace = transform_ops(ops, parameters, outputs, renames) - return env.get_template("ops.c").render(namespace) - - -def render_coords_impl(coords: CoordinateModel): - ops = [] - for s in coords.satisfying: - try: - ops.append(CodeOp(s)) - except: - pass - renames = {"x": "out_x", "y": "out_y"} - for variable in coords.variables: - renames[variable] = "point->{}".format(variable) - for param in coords.curve_model.parameter_names: - renames[param] = "curve->{}".format(param) - namespace = transform_ops(ops, coords.curve_model.parameter_names, - coords.curve_model.coordinate_names, renames) - returns = namespace["returns"] - namespace["returns"] = {} - frees = namespace["frees"] - namespace["frees"] = {} - - return env.get_template("point.c").render(variables=coords.variables, **namespace, - to_affine_rets=returns, to_affine_frees=frees) - - -def render_formula_impl(formula: Formula): - if isinstance(formula, AdditionFormula): - tname = "formula_add.c" - elif isinstance(formula, DoublingFormula): - tname = "formula_dbl.c" - elif isinstance(formula, TriplingFormula): - tname = "formula_tpl.c" - elif isinstance(formula, NegationFormula): - tname = "formula_neg.c" - elif isinstance(formula, ScalingFormula): - tname = "formula_scl.c" - elif isinstance(formula, DifferentialAdditionFormula): - tname = "formula_dadd.c" - elif isinstance(formula, LadderFormula): - tname = "formula_ladd.c" - else: - raise ValueError - template = env.get_template(tname) - inputs = ["one", "other", "diff"] - outputs = ["out_one", "out_other"] - renames = {} - for input in formula.inputs: - var = input[0] - num = int(input[1:]) - formula.input_index - renames[input] = "{}->{}".format(inputs[num], var) - for param in formula.coordinate_model.curve_model.parameter_names: - renames[param] = "curve->{}".format(param) - for output in formula.outputs: - var = output[0] - num = int(output[1:]) - formula.output_index - renames[output] = "{}->{}".format(outputs[num], var) - namespace = transform_ops(formula.code, formula.coordinate_model.curve_model.parameter_names, - formula.outputs, renames) - return template.render(namespace) - - -def render_main(model: CurveModel, coords: CoordinateModel): - return env.get_template("main.c").render(curve_variables=coords.variables, - curve_parameters=model.parameter_names) - - -def render_makefile(platform: Platform, hash_type: HashType, mult_algo: MultAlgo, - mod_rand: RandomMod): - return env.get_template("Makefile").render(platform=str(platform), hash_type=str(hash_type), - mult_algo=str(mult_algo), mod_rand=str(mod_rand)) - - -def save_render(dir: str, fname: str, render: str): - with open(path.join(dir, fname), "w") as f: - f.write(render) - - -def build(platform: Platform, hash_type: HashType, mult_algo: MultAlgo, mod_rand: RandomMod, - model: CurveModel, coords: CoordinateModel, *formulas: Formula): - temp = tempfile.mkdtemp() - symlinks = ["asn1", "bn", "hal", "hash", "mult", "prng", "simpleserial", "tommath", "fat.h", - "point.h", "curve.h", "Makefile.inc"] - for sym in symlinks: - os.symlink(resource_filename("pyecsca.codegen", sym), path.join(temp, sym)) - gen_dir = path.join(temp, "gen") - os.mkdir(gen_dir) - save_render(temp, "Makefile", render_makefile(platform, hash_type, mult_algo, mod_rand)) - save_render(temp, "main.c", render_main(model, coords)) - save_render(gen_dir, "defs.h", render_defs(model, coords)) - point_render = render_coords_impl(coords) - for formula in formulas: - point_render += "\n" - point_render += render_formula_impl(formula) - save_render(gen_dir, "point.c", point_render) - save_render(gen_dir, "curve.c", render_curve_impl(model)) - print(temp) - - -if __name__ == "__main__": - model = ShortWeierstrassModel() - coords = model.coordinates["projective"] - build(Platform.HOST, HashType.SHA1, MultAlgo.DOUBLE_AND_ADD, RandomMod.SAMPLE, model, coords, - coords.formulas["add-1998-cmo"], coords.formulas["dbl-1998-cmo"]) diff --git a/pyecsca/codegen/bn/bn.c b/pyecsca/codegen/bn/bn.c index b34b789..dd22ca1 100644 --- a/pyecsca/codegen/bn/bn.c +++ b/pyecsca/codegen/bn/bn.c @@ -57,7 +57,7 @@ void bn_rand_mod_sample(bn_t *out, const bn_t *mod) { void bn_rand_mod_reduce(bn_t *out, const bn_t *mod) { int mod_len = bn_bit_length(mod); - mp_rand(out, (mod_len / (sizeof(mp_digit) * 8)) + 2); + mp_rand(out, (mod_len / MP_DIGIT_BIT) + 2); mp_mod(out, mod, out); } @@ -110,8 +110,8 @@ bool bn_eq(const bn_t *one, const bn_t *other) { } int bn_get_bit(const bn_t *bn, int which) { - int which_digit = which / (sizeof(mp_digit) * 8); - int which_bit = which % (sizeof(mp_digit) * 8); + int which_digit = which / MP_DIGIT_BIT; + int which_bit = which % MP_DIGIT_BIT; if (bn->used <= which_digit) { return 0; } diff --git a/pyecsca/codegen/builder.py b/pyecsca/codegen/builder.py new file mode 100644 index 0000000..d233bdc --- /dev/null +++ b/pyecsca/codegen/builder.py @@ -0,0 +1,447 @@ +#!/usr/bin/env python3 +import os +import shutil +import subprocess +import tempfile +from ast import operator, Add, Sub, Mult, Div, Pow +from dataclasses import dataclass +from enum import Enum +from os import path +from typing import List, Set, Mapping, Any, Optional, Type, Tuple, MutableMapping + +import click +from jinja2 import Environment, PackageLoader +from pkg_resources import resource_filename +from pyecsca.ec.coordinates import CoordinateModel +from pyecsca.ec.formula import (Formula, AdditionFormula, DoublingFormula, TriplingFormula, + NegationFormula, ScalingFormula, DifferentialAdditionFormula, + LadderFormula) +from pyecsca.ec.model import (CurveModel, ShortWeierstrassModel, MontgomeryModel, EdwardsModel, + TwistedEdwardsModel) +from pyecsca.ec.op import CodeOp + +env = Environment( + loader=PackageLoader("pyecsca.codegen") +) + + +def render_op(op: operator, result: str, left: str, right: str, mod: str) -> Optional[str]: + if isinstance(op, Add): + return "bn_mod_add(&{}, &{}, &{}, &{});".format(left, right, mod, result) + elif isinstance(op, Sub): + return "bn_mod_sub(&{}, &{}, &{}, &{});".format(left, right, mod, result) + elif isinstance(op, Mult): + return "bn_mod_mul(&{}, &{}, &{}, &{});".format(left, right, mod, result) + elif isinstance(op, Div): + return "bn_mod_div(&{}, &{}, &{}, &{});".format(left, right, mod, result) + elif isinstance(op, Pow) and right == 2: + return "bn_mod_sqr(&{}, &{}, &{});".format(left, mod, result) + elif isinstance(op, Pow): + return "bn_mod_pow(&{}, &{}, &{}, &{});".format(left, right, mod, result) + else: + print(op, result, left, right, mod) + + +env.globals["render_op"] = render_op + + +class EnumDefine(Enum): + def __str__(self): + return self.value + + def __repr__(self): + return self.value + + @classmethod + def names(cls): + return list(e.name for e in cls) + + +class Platform(EnumDefine): + HOST = "HOST" + XMEGA = "CW308_XMEGA" + STM32F0 = "CW308_STM32F0" + STM32F3 = "CW308_STM32F3" + + +class Multiplication(EnumDefine): + TOOM_COOK = "MUL_TOOM_COOK" + KARATSUBA = "MUL_KARATSUBA" + COMBA = "MUL_COMBA" + BASE = "MUL_BASE" + + +class Squaring(EnumDefine): + TOOM_COOK = "SQR_TOOM_COOK" + KARATSUBA = "SQR_KARATSUBA" + COMBA = "SQR_COMBA" + BASE = "SQR_BASE" + + +class Reduction(EnumDefine): + BARRETT = "RED_BARRETT" + MONTGOMERY = "RED_MONTGOMERY" + BASE = "RED_BASE" + + +class HashType(EnumDefine): + NONE = "HASH_NONE" + SHA1 = "HASH_SHA1" + SHA224 = "HASH_SHA224" + SHA256 = "HASH_SHA256" + SHA384 = "HASH_SHA384" + SHA512 = "HASH_SHA512" + + +class ScalarMultAlgo(EnumDefine): + NONE = "MULT_NONE" + DOUBLE_AND_ADD = "MULT_DOUBLE_AND_ADD" + + +class RandomMod(EnumDefine): + SAMPLE = "MOD_RAND_SAMPLE" + REDUCE = "MOD_RAND_REDUCE" + + +@dataclass +class Configuration(object): + platform: Platform + hash_type: HashType + mult_algo: ScalarMultAlgo + mod_rand: RandomMod + mult: Multiplication # TODO: Use this + sqr: Squaring # TODO: Use this + red: Reduction # TODO: Use this + model: CurveModel + coords: CoordinateModel + formulas: List[Formula] + + +def render_defs(model: CurveModel, coords: CoordinateModel) -> str: + return env.get_template("defs.h").render(params=model.parameter_names, + variables=coords.variables) + + +def render_curve_impl(model: CurveModel) -> str: + return env.get_template("curve.c").render(params=model.parameter_names) + + +def transform_ops(ops: List[CodeOp], parameters: List[str], outputs: Set[str], + renames: Mapping[str, str] = None) -> MutableMapping[Any, Any]: + def rename(name: str): + if renames is not None and name not in outputs: + return renames.get(name, name) + return name + + allocations = [] + initializations = {} + const_mapping = {} + operations = [] + frees = [] + for op in ops: + if op.result not in allocations: + allocations.append(op.result) + frees.append(op.result) + for param in op.parameters: + if param not in allocations and param not in parameters: + raise ValueError("Should be allocated or parameter: {}".format(param)) + for const in op.constants: + name = "c" + str(const) + if name not in allocations: + allocations.append(name) + initializations[name] = const + const_mapping[const] = name + frees.append(name) + operations.append((op.operator, op.result, rename(op.left), rename(op.right))) + mapped = [] + for op in operations: + o2 = op[2] + if o2 in const_mapping: + o2 = const_mapping[o2] + o3 = op[3] + if o3 in const_mapping and not (isinstance(op[0], Pow) and o3 == 2): + o3 = const_mapping[o3] + mapped.append((op[0], op[1], o2, o3)) + returns = {} + if renames: + for r_from, r_to in renames.items(): + if r_from in outputs: + returns[r_from] = r_to + + return dict(allocations=allocations, + initializations=initializations, + const_mapping=const_mapping, operations=mapped, + frees=frees, returns=returns) + + +def render_ops(ops: List[CodeOp], parameters: List[str], outputs: Set[str], + renames: Mapping[str, str] = None) -> str: + namespace = transform_ops(ops, parameters, outputs, renames) + return env.get_template("ops.c").render(namespace) + + +def render_coords_impl(coords: CoordinateModel) -> str: + ops = [] + for s in coords.satisfying: + try: + ops.append(CodeOp(s)) + except Exception: + pass + renames = {"x": "out_x", "y": "out_y"} + for variable in coords.variables: + renames[variable] = "point->{}".format(variable) + for param in coords.curve_model.parameter_names: + renames[param] = "curve->{}".format(param) + namespace = transform_ops(ops, coords.curve_model.parameter_names, + coords.curve_model.coordinate_names, renames) + returns = namespace["returns"] + namespace["returns"] = {} + frees = namespace["frees"] + namespace["frees"] = {} + + return env.get_template("point.c").render(variables=coords.variables, **namespace, + to_affine_rets=returns, to_affine_frees=frees) + + +def render_formula_impl(formula: Formula) -> str: + if isinstance(formula, AdditionFormula): + tname = "formula_add.c" + elif isinstance(formula, DoublingFormula): + tname = "formula_dbl.c" + elif isinstance(formula, TriplingFormula): + tname = "formula_tpl.c" + elif isinstance(formula, NegationFormula): + tname = "formula_neg.c" + elif isinstance(formula, ScalingFormula): + tname = "formula_scl.c" + elif isinstance(formula, DifferentialAdditionFormula): + tname = "formula_dadd.c" + elif isinstance(formula, LadderFormula): + tname = "formula_ladd.c" + else: + raise ValueError + template = env.get_template(tname) + inputs = ["one", "other", "diff"] + outputs = ["out_one", "out_other"] + renames = {} + for input in formula.inputs: + var = input[0] + num = int(input[1:]) - formula.input_index + renames[input] = "{}->{}".format(inputs[num], var) + for param in formula.coordinate_model.curve_model.parameter_names: + renames[param] = "curve->{}".format(param) + for output in formula.outputs: + var = output[0] + num = int(output[1:]) - formula.output_index + renames[output] = "{}->{}".format(outputs[num], var) + namespace = transform_ops(formula.code, formula.coordinate_model.curve_model.parameter_names, + formula.outputs, renames) + return template.render(namespace) + + +def render_main(model: CurveModel, coords: CoordinateModel) -> str: + return env.get_template("main.c").render(curve_variables=coords.variables, + curve_parameters=model.parameter_names) + + +def render_makefile(platform: Platform, hash_type: HashType, mult_algo: ScalarMultAlgo, + mod_rand: RandomMod) -> str: + return env.get_template("Makefile").render(platform=str(platform), hash_type=str(hash_type), + mult_algo=str(mult_algo), mod_rand=str(mod_rand)) + + +def save_render(dir: str, fname: str, rendered: str): + with open(path.join(dir, fname), "w") as f: + f.write(rendered) + + +def render(config: Configuration) -> Tuple[str, str]: + temp = tempfile.mkdtemp() + symlinks = ["asn1", "bn", "hal", "hash", "mult", "prng", "simpleserial", "tommath", "fat.h", + "point.h", "curve.h", "Makefile.inc"] + for sym in symlinks: + os.symlink(resource_filename("pyecsca.codegen", sym), path.join(temp, sym)) + gen_dir = path.join(temp, "gen") + os.mkdir(gen_dir) + save_render(temp, "Makefile", + render_makefile(config.platform, config.hash_type, config.mult_algo, + config.mod_rand)) + save_render(temp, "main.c", render_main(config.model, config.coords)) + save_render(gen_dir, "defs.h", render_defs(config.model, config.coords)) + point_render = render_coords_impl(config.coords) + for formula in config.formulas: + point_render += "\n" + point_render += render_formula_impl(formula) + save_render(gen_dir, "point.c", point_render) + save_render(gen_dir, "curve.c", render_curve_impl(config.model)) + return temp, "pyecsca-codegen-{}.elf".format(str(config.platform)) + + +def get_model(ctx: click.Context, param, value: str) -> CurveModel: + classes = { + "shortw": ShortWeierstrassModel, + "montgom": MontgomeryModel, + "edwards": EdwardsModel, + "twisted": TwistedEdwardsModel + } + if value not in classes: + raise click.BadParameter("Cannot create CurveModel from '{}'.".format(value)) + model = classes[value]() + ctx.meta["model"] = model + return model + + +def get_coords(ctx: click.Context, param, value: Optional[str]) -> Optional[CoordinateModel]: + if value is None: + return None + model = ctx.meta["model"] + if value not in model.coordinates: + raise click.BadParameter( + "Coordinate model '{}' is not a model in '{}'.".format(value, + model.__class__.__name__)) + coords = model.coordinates[value] + ctx.meta["coords"] = coords + return coords + + +def get_formula(ctx: click.Context, param, value: Optional[Tuple[str]]) -> List[Formula]: + if not value: + return [] + coords = ctx.meta["coords"] + result = [] + for formula in value: + if formula not in coords.formulas: + raise click.BadParameter( + "Formula '{}' is not a formula in '{}'.".format(formula, coords)) + result.append(coords.formulas[formula]) + return result + + +def wrap_enum(enum_class: Type[EnumDefine]): + def callback(ctx, param, value): + try: + res = getattr(enum_class, value) + return res + except Exception: + raise click.BadParameter( + "Cannot create {} enum from {}.".format(enum_class.__name__, value)) + + return callback + + +@click.group(context_settings={"help_option_names": ["-h", "--help"]}) +@click.version_option() +def main(): + pass + + +@main.command() +@click.option("--platform", envvar="PLATFORM", required=True, + type=click.Choice(Platform.names()), + callback=wrap_enum(Platform), + help="The platform to build for.") +@click.option("--hash", envvar="HASH_TYPE", default="SHA1", show_default=True, + type=click.Choice(HashType.names()), + callback=wrap_enum(HashType), + help="The hash algorithm to use (in ECDH and ECDSA).") +@click.option("--mult", envvar="MULT_ALGO", required=True, + type=click.Choice(ScalarMultAlgo.names()), + callback=wrap_enum(ScalarMultAlgo), + help="The scalar multiplication algorithm to use.") +@click.option("--rand", envvar="MOD_RAND", default="SAMPLE", show_default=True, + type=click.Choice(RandomMod.names()), + callback=wrap_enum(RandomMod), + help="The random sampling method to use (for uniform sampling modulo order).") +@click.option("--mul", envvar="MUL", default="BASE", show_default=True, + type=click.Choice(Multiplication.names()), + callback=wrap_enum(Multiplication), + help="Multiplier to use.") +@click.option("--sqr", envvar="SQR", default="BASE", show_default=True, + type=click.Choice(Squaring.names()), + callback=wrap_enum(Squaring), + help="Squaring algorithm to use.") +@click.option("--red", envvar="RED", default="BASE", show_default=True, + type=click.Choice(Reduction.names()), + callback=wrap_enum(Reduction), + help="Modular reduction algorithm to use.") +@click.option("--strip", help="Whether to strip the binary or not.", is_flag=True) +@click.option("--remove/--no-remove", help="Whether to remove the dir.", is_flag=True, default=True) +@click.argument("model", required=True, + type=click.Choice(["shortw", "montgom", "edwards", "twisted"]), + callback=get_model) +@click.argument("coords", required=True, + callback=get_coords) +@click.argument("formulas", required=True, nargs=-1, + callback=get_formula) +@click.argument("outfile") +def build(platform, hash, mult, rand, mul, sqr, red, strip, remove, model, coords, formulas, outfile): + """This command builds an ECC implementation. + + \b + MODEL: The curve model to use. + COORDS: The coordinate model to use. + FORMULAS: The formulas to use. + OUTFILE: The output binary file with the built impl. + """ + config = Configuration(platform, hash, mult, rand, mul, sqr, red, model, coords, formulas) + dir, file = render(config) + + full_path = path.join(dir, file) + res = subprocess.run(["make"], cwd=dir, capture_output=True) + click.echo(res.stdout.decode()) + if strip: + click.echo(path.getsize(full_path)) + subprocess.run(["strip", file], cwd=dir) + click.echo(path.getsize(full_path)) + shutil.copy(full_path, outfile) + if remove: + shutil.rmtree(dir) + else: + click.echo(dir) + + +@main.command() +@click.argument("model", + type=click.Choice(["shortw", "montgom", "edwards", "twisted"]), + callback=get_model) +@click.argument("coords", required=False, + callback=get_coords) +@click.argument("formulas", required=False, nargs=-1, + callback=get_formula) +def list(model: Optional[CurveModel], coords: Optional[CoordinateModel], + formulas: Optional[Tuple[Formula]]): + """This command lists possible choices for an ECC implementation. + + \b + MODEL: The curve model to use. + COORDS: The coordinate model to use. + FORMULAS: The formulas to use. + """ + if formulas: + for formula in formulas: + click.echo(formula) + click.echo("\t{}".format(formula.meta)) + for op in formula.code: + click.echo("\t{}".format(op)) + return + if not formulas and coords: + click.echo(coords) + types = {} + for val in coords.formulas.values(): + category = types.setdefault(val.__class__, []) + category.append(val) + for cls, category in types.items(): + click.echo(cls.__name__) + for form in category: + click.echo("\t {}: {}".format(form.name, form.meta)) + return + if not coords and model: + click.echo(model) + for coord in model.coordinates.values(): + click.echo( + "{}: {}, [{}]".format(coord.name, coord.full_name, ",".join(coord.variables))) + return + + +if __name__ == "__main__": + main() diff --git a/pyecsca/codegen/client.py b/pyecsca/codegen/client.py new file mode 100644 index 0000000..9d839fb --- /dev/null +++ b/pyecsca/codegen/client.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +from binascii import hexlify +from typing import Mapping, Union, Optional + +import click +from pyecsca.ec.coordinates import AffineCoordinateModel +from pyecsca.ec.curve import EllipticCurve +from pyecsca.ec.group import AbelianGroup +from pyecsca.ec.mod import Mod +from pyecsca.ec.model import ShortWeierstrassModel +from pyecsca.ec.point import Point + + +def encode_scalar(val: Union[int, Mod]) -> bytes: + if isinstance(val, int): + return val.to_bytes((val.bit_length() + 7) // 8, "big") + elif isinstance(val, Mod): + return encode_scalar(val.x) + + +def encode_point(point: Point) -> Mapping: + return {var: encode_scalar(value) for var, value in point.coords.items()} + + +def encode_data(name: Optional[str], structure: Union[Mapping, bytes]) -> bytes: + if isinstance(structure, bytes): + header = bytes([ord(name)]) + bytes([len(structure)]) + return header + structure + data = bytes() + for k, v in structure.items(): + data += encode_data(k, v) + if name is not None: + return bytes([ord(name) | 0x80]) + bytes([len(data)]) + data + return data + + +def decode_data(data: bytes) -> Mapping: + result = {} + parsed = 0 + while parsed < len(data): + name = data[parsed] + length = data[parsed + 1] + if name & 0x80: + sub = decode_data(data[parsed + 2: parsed + 2 + length]) + result[chr(name | 0x7f)] = sub + parsed += len(sub) + 2 + else: + result[chr(name)] = data[parsed + 2: parsed + 2 + length] + parsed += length + 2 + return result + + +def cmd_init_prng(seed: bytes) -> str: + return "i" + hexlify(seed).decode() + + +def cmd_set_curve(group: AbelianGroup) -> str: + data = { + "p": encode_scalar(group.curve.prime), + "n": encode_scalar(group.order), + "h": encode_scalar(group.cofactor) + } + for param, value in group.curve.parameters.items(): + data[param] = encode_scalar(value) + data["g"] = encode_point(group.generator) + data["i"] = encode_point(group.neutral) + return "c" + hexlify(encode_data(None, data)).decode() + + +@click.command() +@click.version_option() +def main(): + model = ShortWeierstrassModel() + coords = model.coordinates["projective"] + p = 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff + curve = EllipticCurve(model, coords, + p, + {"a": 0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc, + "b": 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b}) + affine_g = Point(AffineCoordinateModel(model), + x=Mod(0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296, p), + y=Mod(0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5, p)) + g = Point.from_affine(coords, affine_g) + neutral = Point(coords, X=Mod(0, p), Y=Mod(1, p), Z=Mod(0, p)) + group = AbelianGroup(curve, g, neutral, + 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551, 0x1) + print(cmd_set_curve(group)) + + +if __name__ == "__main__": + main() diff --git a/pyecsca/codegen/fat.h b/pyecsca/codegen/fat.h index e9fe3bd..b1c094b 100644 --- a/pyecsca/codegen/fat.h +++ b/pyecsca/codegen/fat.h @@ -1,6 +1,11 @@ +#ifndef FAT_H_ +#define FAT_H_ + typedef struct { size_t len; void *value; } fat_t; -#define fat_empty {0, NULL}
\ No newline at end of file +#define fat_empty {0, NULL} + +#endif //FAT_H_
\ No newline at end of file diff --git a/pyecsca/codegen/templates/main.c b/pyecsca/codegen/templates/main.c index ac620b5..14a4e58 100644 --- a/pyecsca/codegen/templates/main.c +++ b/pyecsca/codegen/templates/main.c @@ -93,7 +93,7 @@ static uint8_t cmd_generate(uint8_t *data, uint16_t len) { uint8_t priv[priv_size]; bn_to_bin(&privkey, priv); simpleserial_put('s', priv_size, priv); - uint8_t pub[coord_size * {{ curve_parameters | length }}]; + uint8_t pub[coord_size * {{ curve_variables | length }}]; {%- for variable in curve_variables %} bn_to_binpad(&pubkey->{{ variable }}, pub + coord_size * {{ loop.index0 }}, coord_size); {%- endfor %} @@ -147,7 +147,7 @@ static uint8_t cmd_scalar_mult(uint8_t *data, uint16_t len) { scalar_mult(&scalar, curve->generator, curve, result); - uint8_t res[coord_size * {{ curve_parameters | length }}]; + uint8_t res[coord_size * {{ curve_variables | length }}]; {%- for variable in curve_variables %} bn_to_binpad(&result->{{ variable }}, res + coord_size * {{ loop.index0 }}, coord_size); {%- endfor %} |
