aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJ08nY2019-12-23 13:20:43 +0100
committerJ08nY2019-12-23 13:42:05 +0100
commit276ce0d7d636009299319aad27e31de242e1cceb (patch)
tree894d92ba07e9d9565b0343c11a8c8ca8fe039865
parentb43c5dba0ec18fe5a5204537855ea2b73fc674c6 (diff)
downloadpyecsca-codegen-276ce0d7d636009299319aad27e31de242e1cceb.tar.gz
pyecsca-codegen-276ce0d7d636009299319aad27e31de242e1cceb.tar.zst
pyecsca-codegen-276ce0d7d636009299319aad27e31de242e1cceb.zip
Add Travis and Codecov + tests.
-rw-r--r--.coveragerc17
-rw-r--r--.gitignore6
-rw-r--r--.travis.yml25
-rw-r--r--MANIFEST.in10
-rw-r--r--README.md2
-rw-r--r--codecov.yml8
-rw-r--r--pyecsca/codegen/builder.py48
-rw-r--r--setup.py6
-rw-r--r--test/__init__.py0
-rw-r--r--test/test_builder.py58
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
diff --git a/.gitignore b/.gitignore
index 91c8a51..816237f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/README.md b/README.md
index c660b7e..029f5e6 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
# pyecsca-codegen
+[![Build Status](https://travis-ci.com/J08nY/pyecsca-codegen.svg?branch=master)](https://travis-ci.com/J08nY/pyecsca-codegen) ![License: MIT](https://img.shields.io/github/license/J08nY/pyecsca.svg) [![codecov](https://codecov.io/gh/J08nY/pyecsca-codegen/branch/master/graph/badge.svg)](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.
diff --git a/setup.py b/setup.py
index 720892c..0d57557 100644
--- a/setup.py
+++ b/setup.py
@@ -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)