diff options
| author | J08nY | 2019-12-23 13:20:43 +0100 |
|---|---|---|
| committer | J08nY | 2019-12-23 13:42:05 +0100 |
| commit | 276ce0d7d636009299319aad27e31de242e1cceb (patch) | |
| tree | 894d92ba07e9d9565b0343c11a8c8ca8fe039865 | |
| parent | b43c5dba0ec18fe5a5204537855ea2b73fc674c6 (diff) | |
| download | pyecsca-codegen-276ce0d7d636009299319aad27e31de242e1cceb.tar.gz pyecsca-codegen-276ce0d7d636009299319aad27e31de242e1cceb.tar.zst pyecsca-codegen-276ce0d7d636009299319aad27e31de242e1cceb.zip | |
Add Travis and Codecov + tests.
| -rw-r--r-- | .coveragerc | 17 | ||||
| -rw-r--r-- | .gitignore | 6 | ||||
| -rw-r--r-- | .travis.yml | 25 | ||||
| -rw-r--r-- | MANIFEST.in | 10 | ||||
| -rw-r--r-- | README.md | 2 | ||||
| -rw-r--r-- | codecov.yml | 8 | ||||
| -rw-r--r-- | pyecsca/codegen/builder.py | 48 | ||||
| -rw-r--r-- | setup.py | 6 | ||||
| -rw-r--r-- | test/__init__.py | 0 | ||||
| -rw-r--r-- | test/test_builder.py | 58 |
10 files changed, 168 insertions, 12 deletions
diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..33d3f05 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,17 @@ +[run] +branch = True +omit = + test/* + setup.py + pyecsca/codegen/templates/* + ext/pyecsca/* + +[report] +exclude_lines = + __repr__ + pragma: no cover + raise AssertionError + raise NotImplementedError + if 0: + if __name__ == .__main__.: + \.\.\.$
\ No newline at end of file @@ -1,2 +1,6 @@ /pyecsca_codegen.egg-info -/pyecsca/codegen/tommath/
\ No newline at end of file +/pyecsca/codegen/tommath/ +.coverage +htmlcov/ + +__pycache__
\ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..9440757 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,25 @@ +language: python +dist: xenial +python: + - "3.7" + - "3.8" + +install: + - pip install codecov + - mkdir pyecsca/codegen/tommath + - cd ext/libtommath/ + - make + - cp libtommath.a ../../pyecsca/codegen/tommath/libtommath-HOST.a + - cp tommath.h ../../pyecsca/codegen/tommath/tommath.h + - cd ../ + - git clone https://github.com/J08nY/pyecsca + - cd pyecsca + - pip install -e . + - cd ../.. + - pip install -e ".[test, typecheck]" + +script: + - nose2 -s test -C -v test_builder + +after_success: + - codecov diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..b47afac --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,10 @@ +include README.md +graft pyecsca/codegen/asn1 +graft pyecsca/codegen/bn +graft pyecsca/codegen/hal +graft pyecsca/codegen/hash +graft pyecsca/codegen/prng +graft pyecsca/codegen/simpleserial +graft pyecsca/codegen/templates +graft pyecsca/codegen/tommath +recursive-include pyecsca/codegen *.h *.inc
\ No newline at end of file @@ -1,5 +1,7 @@ # pyecsca-codegen +[](https://travis-ci.com/J08nY/pyecsca-codegen)  [](https://codecov.io/gh/J08nY/pyecsca-codegen) + **Py**thon **E**lliptic **C**urve cryptography **S**ide-**C**hannel **A**nalysis toolkit. Codegen package. See the [main repo](https://github.com/J08nY/pyecsca) for more information. diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..475761f --- /dev/null +++ b/codecov.yml @@ -0,0 +1,8 @@ +coverage: + range: 50..100 + round: up + precision: 2 + +ignore: + - "test/*.py" + - "setup.py"
\ No newline at end of file diff --git a/pyecsca/codegen/builder.py b/pyecsca/codegen/builder.py index 366ee18..e7e26ba 100644 --- a/pyecsca/codegen/builder.py +++ b/pyecsca/codegen/builder.py @@ -14,6 +14,7 @@ from typing import List, Set, Mapping, Any, Optional, Type, Tuple, MutableMappin import click from jinja2 import Environment, PackageLoader from pkg_resources import resource_filename +from public import public from pyecsca.ec.coordinates import CoordinateModel from pyecsca.ec.formula import (Formula, AdditionFormula, DoublingFormula, TriplingFormula, NegationFormula, ScalingFormula, DifferentialAdditionFormula, @@ -65,6 +66,7 @@ class EnumDefine(Enum): return list(e.name for e in cls) +@public class Platform(EnumDefine): """Platform to build for.""" HOST = "HOST" @@ -73,6 +75,7 @@ class Platform(EnumDefine): STM32F3 = "CW308_STM32F3" +@public class Multiplication(EnumDefine): """Base multiplication algorithm to use.""" TOOM_COOK = "MUL_TOOM_COOK" @@ -81,6 +84,7 @@ class Multiplication(EnumDefine): BASE = "MUL_BASE" +@public class Squaring(EnumDefine): """Base squaring algorithm to use.""" TOOM_COOK = "SQR_TOOM_COOK" @@ -89,6 +93,7 @@ class Squaring(EnumDefine): BASE = "SQR_BASE" +@public class Reduction(EnumDefine): """Modular reduction method used.""" BARRETT = "RED_BARRETT" @@ -96,6 +101,7 @@ class Reduction(EnumDefine): BASE = "RED_BASE" +@public class HashType(EnumDefine): """Hash algorithm used in ECDH and ECDSA.""" NONE = "HASH_NONE" @@ -106,12 +112,14 @@ class HashType(EnumDefine): 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 @@ -251,8 +259,10 @@ def render_formula_impl(formula: Formula, short_circuit: bool = False) -> str: def render_scalarmult_impl(scalarmult: ScalarMultiplier) -> str: return env.get_template("mult.c").render(scalarmult=scalarmult, LTRMultiplier=LTRMultiplier, - RTLMultiplier=RTLMultiplier, CoronMultiplier=CoronMultiplier, - LadderMultiplier=LadderMultiplier, SimpleLadderMultiplier=SimpleLadderMultiplier, + RTLMultiplier=RTLMultiplier, + CoronMultiplier=CoronMultiplier, + LadderMultiplier=LadderMultiplier, + SimpleLadderMultiplier=SimpleLadderMultiplier, DifferentialLadderMultiplier=DifferentialLadderMultiplier, BinaryNAFMultiplier=BinaryNAFMultiplier) @@ -272,6 +282,7 @@ def save_render(dir: str, fname: str, rendered: str): f.write(rendered) +@public def render(config: Configuration) -> Tuple[str, str]: temp = tempfile.mkdtemp() symlinks = ["asn1", "bn", "hal", "hash", "mult", "prng", "simpleserial", "tommath", "fat.h", @@ -294,6 +305,19 @@ def render(config: Configuration) -> Tuple[str, str]: return temp, "pyecsca-codegen-{}.elf".format(str(config.platform)) +@public +def render_and_build(config, outfile, strip=False, remove=True): + dir, file = render(config) + + full_path = path.join(dir, file) + res = subprocess.run(["make"], cwd=dir, capture_output=True) + if strip: + subprocess.run(["strip", file], cwd=dir) + shutil.copy(full_path, outfile) + if remove: + shutil.rmtree(dir) + + def get_model(ctx: click.Context, param, value: str) -> CurveModel: classes = { "shortw": ShortWeierstrassModel, @@ -341,8 +365,8 @@ def get_multiplier(ctx: click.Context, param, value: Optional[str]) -> Optional[ if value is None: return None res = re.match( - "(?P<name>[a-zA-Z\-]+)\((?P<args>([a-zA-Z_]+ *= *[a-zA-Z0-9]+, )*?([a-zA-Z_]+ *= *[a-zA-Z0-9]+)*)\)", - value) + "(?P<name>[a-zA-Z\-]+)\((?P<args>([a-zA-Z_]+ *= *[a-zA-Z0-9]+, )*?([a-zA-Z_]+ *= *[a-zA-Z0-9]+)*)\)", + value) if not res: raise click.BadParameter("Couldn't parse multiplier spec: {}.".format(value)) name = res.group("name") @@ -370,15 +394,15 @@ def get_multiplier(ctx: click.Context, param, value: Optional[str]) -> Optional[ if not all( any(issubclass(cls, required) for cls in classes) for required in mult_class.requires): raise click.BadParameter( - "Multiplier {} requires formulas: {}, got {}.".format(mult_class.__name__, - mult_class.requires, classes)) + "Multiplier {} requires formulas: {}, got {}.".format(mult_class.__name__, + mult_class.requires, classes)) kwargs = eval("dict(" + args + ")") required = set( - filter(lambda formula: any(isinstance(formula, cls) for cls in mult_class.requires), - formulas)) + filter(lambda formula: any(isinstance(formula, cls) for cls in mult_class.requires), + formulas)) optional = set( - filter(lambda formula: any(isinstance(formula, cls) for cls in mult_class.optionals), - formulas)) + filter(lambda formula: any(isinstance(formula, cls) for cls in mult_class.optionals), + formulas)) for formula in required.union(optional): kwargs[formula.shortname] = formula mult = mult_class(**kwargs) @@ -397,8 +421,10 @@ def wrap_enum(enum_class: Type[EnumDefine]): return callback + @click.group(context_settings={"help_option_names": ["-h", "--help"]}) @click.version_option() +@public def main(): pass @@ -440,6 +466,7 @@ def main(): @click.argument("scalarmult", required=True, callback=get_multiplier) @click.argument("outfile") +@public def build(platform, hash, rand, mul, sqr, red, strip, remove, model, coords, formulas, scalarmult, outfile): """This command builds an ECC implementation. @@ -477,6 +504,7 @@ def build(platform, hash, rand, mul, sqr, red, strip, remove, model, coords, for callback=get_coords) @click.argument("formulas", required=False, nargs=-1, callback=get_formula) +@public def list(model: Optional[CurveModel], coords: Optional[CoordinateModel], formulas: Optional[Tuple[Formula]]): """This command lists possible choices for an ECC implementation. @@ -20,7 +20,11 @@ setup( "Intended Audience :: Developers", "Intended Audience :: Science/Research" ], - install_package_data=True, + package_data={ + "pyecsca.codegen" : ["*.h", "*.inc", "asn1/", "bn/", "hal/", "hash/", "prng/", "simpleserial/", + "templates/", "tommath/"] + }, + #install_package_data=True, python_requires='>=3.7', install_requires=[ "pyecsca", diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/__init__.py diff --git a/test/test_builder.py b/test/test_builder.py new file mode 100644 index 0000000..f5e32f6 --- /dev/null +++ b/test/test_builder.py @@ -0,0 +1,58 @@ +import tempfile +from os.path import join +from unittest import TestCase + +from click.testing import CliRunner +from pyecsca.ec.curves import get_curve +from pyecsca.ec.mod import Mod +from pyecsca.ec.mult import LTRMultiplier +from pyecsca.ec.point import Point + +from pyecsca.codegen.builder import * + + +class BuilderTests(TestCase): + + def test_basic_build(self): + platform = Platform.HOST + hash_type = HashType.SHA1 + mod_rand = RandomMod.REDUCE + mult = Multiplication.BASE + sqr = Squaring.BASE + red = Reduction.BASE + group = get_curve("secp128r1", "projective") + model = group.curve.model + coords = group.curve.coordinate_model + group.neutral = Point(coords, X=Mod(0, group.curve.prime), Y=Mod(1, group.curve.prime), + Z=Mod(0, group.curve.prime)) + add = coords.formulas["add-1998-cmo"] + dbl = coords.formulas["dbl-1998-cmo"] + scl = coords.formulas["z"] + formulas = [add, dbl, scl] + scalarmult = LTRMultiplier(add, dbl, scl) + config = Configuration(platform, hash_type, mod_rand, mult, sqr, red, model, coords, + formulas, scalarmult) + temp = tempfile.mkdtemp() + binary = join(temp, "test.elf") + render_and_build(config, binary) + + def test_cli_build(self): + runner = CliRunner() + with runner.isolated_filesystem(): + result = runner.invoke(build, + ["--platform", "HOST", "shortw", "projective", "add-1998-cmo", + "dbl-1998-cmo", "z", "ltr(complete=True)", "test.elf"]) + self.assertEqual(result.exit_code, 0) + result = runner.invoke(build, ["--platform", "HOST", "--strip", "shortw", "projective", + "add-1998-cmo", "dbl-1998-cmo", "z", "ltr(complete=True)", + "test_stripped.elf"]) + self.assertEqual(result.exit_code, 0) + + def test_cli_list(self): + runner = CliRunner() + result = runner.invoke(list, ["montgom"]) + self.assertEqual(result.exit_code, 0) + result = runner.invoke(list, ["montgom", "xz"]) + self.assertEqual(result.exit_code, 0) + result = runner.invoke(list, ["montgom", "xz", "ladd-1987-m"]) + self.assertEqual(result.exit_code, 0) |
