aboutsummaryrefslogtreecommitdiffhomepage
path: root/pyecsca/codegen/builder.py
diff options
context:
space:
mode:
Diffstat (limited to 'pyecsca/codegen/builder.py')
-rw-r--r--pyecsca/codegen/builder.py225
1 files changed, 64 insertions, 161 deletions
diff --git a/pyecsca/codegen/builder.py b/pyecsca/codegen/builder.py
index 3970d21..d65eb2b 100644
--- a/pyecsca/codegen/builder.py
+++ b/pyecsca/codegen/builder.py
@@ -4,12 +4,10 @@ import re
import shutil
import subprocess
import tempfile
-from ast import operator, Add, Sub, Mult, Div, Pow
+from ast import Pow
from copy import copy
-from dataclasses import dataclass
-from enum import Enum
from os import path
-from typing import List, Set, Mapping, Any, Optional, Type, Tuple, MutableMapping
+from typing import List, Set, Mapping, Any, Optional, Tuple, MutableMapping
import click
from jinja2 import Environment, PackageLoader
@@ -23,152 +21,41 @@ from pyecsca.ec.model import (CurveModel, ShortWeierstrassModel, MontgomeryModel
TwistedEdwardsModel)
from pyecsca.ec.mult import (ScalarMultiplier, LTRMultiplier, RTLMultiplier, CoronMultiplier,
LadderMultiplier, SimpleLadderMultiplier, DifferentialLadderMultiplier,
- WindowNAFMultiplier, BinaryNAFMultiplier)
-from pyecsca.ec.op import CodeOp
+ BinaryNAFMultiplier)
+from pyecsca.ec.op import CodeOp, OpType
+
+from .common import (Platform, Multiplication, Squaring, Reduction, HashType, RandomMod,
+ Configuration, MULTIPLIERS, wrap_enum)
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):
+def render_op(op: OpType, result: str, left: str, right: str, mod: str) -> Optional[str]:
+ if op == OpType.Add:
return "bn_mod_add(&{}, &{}, &{}, &{});".format(left, right, mod, result)
- elif isinstance(op, Sub):
+ elif op == OpType.Sub:
return "bn_mod_sub(&{}, &{}, &{}, &{});".format(left, right, mod, result)
- elif isinstance(op, Mult):
+ elif op == OpType.Mult:
return "bn_mod_mul(&{}, &{}, &{}, &{});".format(left, right, mod, result)
- elif isinstance(op, Div):
+ elif op == OpType.Div or op == OpType.Inv:
return "bn_mod_div(&{}, &{}, &{}, &{});".format(left, right, mod, result)
- elif isinstance(op, Pow) and right == 2:
+ elif op == OpType.Sqr:
return "bn_mod_sqr(&{}, &{}, &{});".format(left, mod, result)
- elif isinstance(op, Pow):
+ elif op == OpType.Pow:
return "bn_mod_pow(&{}, &{}, &{}, &{});".format(left, right, mod, result)
- elif op is None:
+ elif op == OpType.Id:
return "bn_copy(&{}, &{});".format(left, result)
else:
print(op, result, left, right, mod)
+ return None
env.globals["render_op"] = render_op
env.globals["isinstance"] = isinstance
-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)
-
-
-@public
-class Platform(EnumDefine):
- """Platform to build for."""
- HOST = "HOST"
- XMEGA = "CW308_XMEGA"
- STM32F0 = "CW308_STM32F0"
- STM32F3 = "CW308_STM32F3"
-
-
-@public
-class Multiplication(EnumDefine):
- """Base multiplication algorithm to use."""
- TOOM_COOK = "MUL_TOOM_COOK"
- KARATSUBA = "MUL_KARATSUBA"
- COMBA = "MUL_COMBA"
- BASE = "MUL_BASE"
-
-
-@public
-class Squaring(EnumDefine):
- """Base squaring algorithm to use."""
- TOOM_COOK = "SQR_TOOM_COOK"
- KARATSUBA = "SQR_KARATSUBA"
- COMBA = "SQR_COMBA"
- BASE = "SQR_BASE"
-
-
-@public
-class Reduction(EnumDefine):
- """Modular reduction method used."""
- BARRETT = "RED_BARRETT"
- MONTGOMERY = "RED_MONTGOMERY"
- BASE = "RED_BASE"
-
-
-@public
-class HashType(EnumDefine):
- """Hash algorithm used in ECDH and ECDSA."""
- NONE = "HASH_NONE"
- SHA1 = "HASH_SHA1"
- SHA224 = "HASH_SHA224"
- SHA256 = "HASH_SHA256"
- SHA384 = "HASH_SHA384"
- SHA512 = "HASH_SHA512"
-
-
-@public
-class RandomMod(EnumDefine):
- """Method of sampling a uniform integer modulo order."""
- SAMPLE = "MOD_RAND_SAMPLE"
- REDUCE = "MOD_RAND_REDUCE"
-
-
-@public
-@dataclass
-class Configuration(object):
- platform: Platform
- hash_type: HashType
- 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]
- scalarmult: ScalarMultiplier
-
-MULTIPLIERS = [
- {
- "name": ("rtl", "RTLMultiplier"),
- "class": LTRMultiplier
- },
- {
- "name": ("rtl", "RTLMultiplier"),
- "class": RTLMultiplier
- },
- {
- "name": ("coron", "CoronMultiplier"),
- "class": CoronMultiplier
- },
- {
- "name":("ldr", "LadderMultiplier"),
- "class": LadderMultiplier
- },
- {
- "name": ("simple-ldr", "SimpleLadderMultiplier"),
- "class": SimpleLadderMultiplier
- },
- {
- "name": ("diff-ldr", "DifferentialLadderMultiplier"),
- "class": DifferentialLadderMultiplier
- },
- {
- "name": ("naf", "bnaf", "BinaryNAFMultiplier"),
- "class": BinaryNAFMultiplier
- },
- {
- "name": ("wnaf", "WindowNAFMultiplier"),
- "class": WindowNAFMultiplier
- }
-]
-
-
def render_defs(model: CurveModel, coords: CoordinateModel) -> str:
return env.get_template("defs.h").render(params=model.parameter_names,
variables=coords.variables)
@@ -185,7 +72,7 @@ def transform_ops(ops: List[CodeOp], parameters: List[str], outputs: Set[str],
return renames.get(name, name)
return name
- allocations = []
+ allocations: List[str] = []
initializations = {}
const_mapping = {}
operations = []
@@ -302,9 +189,10 @@ def render_scalarmult_impl(scalarmult: ScalarMultiplier) -> str:
BinaryNAFMultiplier=BinaryNAFMultiplier)
-def render_main(model: CurveModel, coords: CoordinateModel) -> str:
+def render_main(model: CurveModel, coords: CoordinateModel, keygen: bool, ecdh: bool, ecdsa: bool) -> str:
return env.get_template("main.c").render(curve_variables=coords.variables,
- curve_parameters=model.parameter_names)
+ curve_parameters=model.parameter_names,
+ keygen=keygen, ecdh=ecdh, ecdsa=ecdsa)
def render_makefile(platform: Platform, hash_type: HashType, mod_rand: RandomMod) -> str:
@@ -328,7 +216,7 @@ def render(config: Configuration) -> Tuple[str, str, str]:
os.mkdir(gen_dir)
save_render(temp, "Makefile",
render_makefile(config.platform, config.hash_type, config.mod_rand))
- save_render(temp, "main.c", render_main(config.model, config.coords))
+ save_render(temp, "main.c", render_main(config.model, config.coords, config.keygen, config.ecdh, config.ecdsa))
save_render(gen_dir, "defs.h", render_defs(config.model, config.coords))
point_render = render_coords_impl(config.coords)
for formula in config.formulas:
@@ -337,7 +225,8 @@ def render(config: Configuration) -> Tuple[str, str, str]:
save_render(gen_dir, "point.c", point_render)
save_render(gen_dir, "curve.c", render_curve_impl(config.model))
save_render(gen_dir, "mult.c", render_scalarmult_impl(config.scalarmult))
- return temp, "pyecsca-codegen-{}.elf".format(str(config.platform)), "pyecsca-codegen-{}.hex".format(str(config.platform))
+ return temp, "pyecsca-codegen-{}.elf".format(
+ str(config.platform)), "pyecsca-codegen-{}.hex".format(str(config.platform))
@public
@@ -439,23 +328,21 @@ def get_multiplier(ctx: click.Context, param, value: Optional[str]) -> Optional[
return mult
-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
-
+def get_ecdsa(ctx: click.Context, param, value: bool) -> bool:
+ if not value:
+ return False
+ formulas = ctx.meta["formulas"]
+ if not any(isinstance(formula, AdditionFormula) for formula in formulas):
+ raise click.BadParameter("ECDSA needs an addition formula. None was supplied.")
@click.group(context_settings={"help_option_names": ["-h", "--help"]})
@click.version_option()
@public
def main():
+ """
+ A tool for building, querying and flashing ECC implementations on devices.
+ """
pass
@@ -484,6 +371,10 @@ def main():
type=click.Choice(Reduction.names()),
callback=wrap_enum(Reduction),
help="Modular reduction algorithm to use.")
+@click.option("--keygen/--no-keygen", help="Whether to disable keygen.", is_flag=True, default=True)
+@click.option("--ecdh/--no-ecdh", help="Whether to disable ECDH.", is_flag=True, default=True)
+@click.option("--ecdsa/--no-ecdsa", help="Whether to disable ECDSA.", is_flag=True, default=True,
+ callback=get_ecdsa)
@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.option("-v", "--verbose", count=True)
@@ -498,8 +389,8 @@ def main():
callback=get_multiplier)
@click.argument("outdir")
@public
-def build_impl(platform, hash, rand, mul, sqr, red, strip, remove, verbose, model, coords, formulas, scalarmult,
- outdir):
+def build_impl(platform, hash, rand, mul, sqr, red, keygen, ecdh, ecdsa, strip, remove,
+ verbose, model, coords, formulas, scalarmult, outdir):
"""This command builds an ECC implementation.
\b
@@ -510,10 +401,10 @@ def build_impl(platform, hash, rand, mul, sqr, red, strip, remove, verbose, mode
OUTDIR: The output directory for files with the built impl.
"""
- config = Configuration(platform, hash, rand, mul, sqr, red, model, coords, formulas, scalarmult)
+ config = Configuration(platform, hash, rand, mul, sqr, red, model, coords, formulas, scalarmult,
+ keygen, ecdh, ecdsa)
dir, elf_file, hex_file = render(config)
-
res = subprocess.run(["make"], cwd=dir, capture_output=True)
if verbose >= 1:
click.echo(res.stdout.decode())
@@ -540,7 +431,7 @@ def build_impl(platform, hash, rand, mul, sqr, red, strip, remove, verbose, mode
callback=get_formula)
@public
def list_impl(model: Optional[CurveModel], coords: Optional[CoordinateModel],
- formulas: Optional[Tuple[Formula]]):
+ formulas: Optional[Tuple[Formula]]):
"""This command lists possible choices for an ECC implementation.
If no arguments are provided the argument lists other implementation options,
such as modular reduction algorithms, build platforms and so on.
@@ -575,14 +466,24 @@ def list_impl(model: Optional[CurveModel], coords: Optional[CoordinateModel],
"{}: {}, [{}]".format(coord.name, coord.full_name, ",".join(coord.variables)))
return
if not model:
- click.echo(click.wrap_text("Platform:\n\t" + ", ".join(Platform.names()),subsequent_indent="\t"))
- click.echo(click.wrap_text("Hash type:\n\t" + ", ".join(HashType.names()),subsequent_indent="\t"))
- click.echo(click.wrap_text("Modular Random:\n\t" + ", ".join(RandomMod.names()),subsequent_indent="\t"))
- click.echo(click.wrap_text("Multiplication:\n\t" + ", ".join(Multiplication.names()),subsequent_indent="\t"))
- click.echo(click.wrap_text("Squaring:\n\t" + ", ".join(Squaring.names()),subsequent_indent="\t"))
- click.echo(click.wrap_text("Modular Reduction:\n\t" + ", ".join(Reduction.names()),subsequent_indent="\t"))
- click.echo(click.wrap_text("Scalar multplier:\n\t" + ", ".join(map(lambda m: m["name"][-1], MULTIPLIERS)),
+ click.echo(
+ click.wrap_text("Platform:\n\t" + ", ".join(Platform.names()),
+ subsequent_indent="\t"))
+ click.echo(
+ click.wrap_text("Hash type:\n\t" + ", ".join(HashType.names()),
+ subsequent_indent="\t"))
+ click.echo(click.wrap_text("Modular Random:\n\t" + ", ".join(RandomMod.names()),
+ subsequent_indent="\t"))
+ click.echo(click.wrap_text("Multiplication:\n\t" + ", ".join(Multiplication.names()),
+ subsequent_indent="\t"))
+ click.echo(
+ click.wrap_text("Squaring:\n\t" + ", ".join(Squaring.names()),
+ subsequent_indent="\t"))
+ click.echo(click.wrap_text("Modular Reduction:\n\t" + ", ".join(Reduction.names()),
subsequent_indent="\t"))
+ click.echo(click.wrap_text(
+ "Scalar multplier:\n\t" + ", ".join(map(lambda m: m["name"][-1], MULTIPLIERS)),
+ subsequent_indent="\t"))
@main.command()
@@ -592,7 +493,7 @@ def list_impl(model: Optional[CurveModel], coords: Optional[CoordinateModel],
help="The platform to flash.")
@click.argument("dir")
@public
-def flash(platform, dir):
+def flash(platform, dir): # pragma: no cover
"""This command flashes a chip through the ChipWhisperer framework with the built implementation.
\b
@@ -602,14 +503,16 @@ def flash(platform, dir):
import chipwhisperer as cw
except ImportError:
click.secho("ChipWhisperer not installed, flashing requires it.", fg="red", err=True)
- return
+ raise click.Abort
if platform in (Platform.STM32F0, Platform.STM32F3):
prog = cw.programmers.STM32FProgrammer
elif platform == Platform.XMEGA:
prog = cw.programmers.XMEGAProgrammer
else:
- click.secho("Flashing the HOST is not required, just run the ELF and communicate with it via the standard IO.", fg="red", err=True)
- return
+ click.secho(
+ "Flashing the HOST is not required, just run the ELF and communicate with it via the standard IO.",
+ fg="red", err=True)
+ raise click.Abort
fw_path = path.join(dir, "pyecsca-codegen-{}.hex".format(platform))
scope = cw.scope()
scope.default_setup()