aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJ08nY2020-02-19 18:29:53 +0100
committerJ08nY2020-02-19 18:29:53 +0100
commit5da1d167c203395103d220c450e29fece08f4198 (patch)
treefdc8fb7f0fbf2d2aaab46c2cb8a90a8176058292
parentf04c640c05e9ffe894f67194832623e28a8000f5 (diff)
downloadpyecsca-codegen-5da1d167c203395103d220c450e29fece08f4198.tar.gz
pyecsca-codegen-5da1d167c203395103d220c450e29fece08f4198.tar.zst
pyecsca-codegen-5da1d167c203395103d220c450e29fece08f4198.zip
Flesh out client, add implementation tests.
-rw-r--r--Makefile2
-rw-r--r--pyecsca/codegen/builder.py23
-rw-r--r--pyecsca/codegen/client.py191
-rw-r--r--pyecsca/codegen/common.py2
-rw-r--r--pyecsca/codegen/point.h6
-rw-r--r--pyecsca/codegen/render.py11
-rw-r--r--pyecsca/codegen/templates/main.c12
-rw-r--r--pyecsca/codegen/templates/mult.c14
-rw-r--r--pyecsca/codegen/templates/mult_ltr.c3
-rw-r--r--pyecsca/codegen/templates/point.c34
-rw-r--r--test/test_builder.py26
-rw-r--r--test/test_client.py13
-rw-r--r--test/test_impl.py77
-rw-r--r--test/test_render.py32
14 files changed, 319 insertions, 127 deletions
diff --git a/Makefile b/Makefile
index 3a7853b..42858a7 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-TESTS = test_builder test_client
+TESTS = test_builder test_client test_render test_impl
test:
nose2 -s test -A !slow -C -v ${TESTS}
diff --git a/pyecsca/codegen/builder.py b/pyecsca/codegen/builder.py
index b1a8816..b9f9708 100644
--- a/pyecsca/codegen/builder.py
+++ b/pyecsca/codegen/builder.py
@@ -4,7 +4,7 @@ import shutil
import subprocess
from copy import copy
from os import path
-from typing import List, Optional, Tuple
+from typing import List, Optional, Tuple, Type, MutableMapping
import click
from public import public
@@ -39,13 +39,13 @@ 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]+)*)\)",
+ "(?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")
args = res.group("args")
- mult_class = None
+ mult_class: Type[ScalarMultiplier] = None
for mult_def in MULTIPLIERS:
if name in mult_def["name"]:
mult_class = mult_def["class"]
@@ -80,6 +80,7 @@ def get_ecdsa(ctx: click.Context, param, value: bool) -> bool:
formulas = ctx.obj["formulas"]
if not any(isinstance(formula, AdditionFormula) for formula in formulas):
raise click.BadParameter("ECDSA needs an addition formula. None was supplied.")
+ return value
@click.group(context_settings={"help_option_names": ["-h", "--help"]})
@@ -108,7 +109,7 @@ def main():
@click.option("--mul", envvar="MUL", default="BASE", show_default=True,
type=click.Choice(Multiplication.names()),
callback=wrap_enum(Multiplication),
- help="Multiplier to use.")
+ help="Multiplication algorithm to use.")
@click.option("--sqr", envvar="SQR", default="BASE", show_default=True,
type=click.Choice(Squaring.names()),
callback=wrap_enum(Squaring),
@@ -117,12 +118,12 @@ 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("--keygen/--no-keygen", help="Whether to enable keygen.", is_flag=True, default=True, show_default=True)
+@click.option("--ecdh/--no-ecdh", help="Whether to enable ECDH.", is_flag=True, default=True, show_default=True)
+@click.option("--ecdsa/--no-ecdsa", help="Whether to enable ECDSA.", is_flag=True, default=True,
+ callback=get_ecdsa, show_default=True)
@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("--remove/--no-remove", help="Whether to remove the dir.", is_flag=True, default=True, show_default=True)
@click.option("-v", "--verbose", count=True)
@click.argument("model", required=True,
type=click.Choice(["shortw", "montgom", "edwards", "twisted"]),
@@ -143,7 +144,7 @@ def build_impl(platform, hash, rand, mul, sqr, red, keygen, ecdh, ecdsa, strip,
MODEL: The curve model to use.
COORDS: The coordinate model to use.
FORMULAS: The formulas to use.
- MULT: The scalar multiplication algorithm to use.
+ SCALARMULT: The scalar multiplication algorithm to use.
OUTDIR: The output directory for files with the built impl.
"""
@@ -196,7 +197,7 @@ def list_impl(model: Optional[CurveModel], coords: Optional[CoordinateModel],
return
if not formulas and coords:
click.echo(coords)
- types = {}
+ types: MutableMapping[Type, List] = {}
for val in coords.formulas.values():
category = types.setdefault(val.__class__, [])
category.append(val)
diff --git a/pyecsca/codegen/client.py b/pyecsca/codegen/client.py
index 81dca40..197c703 100644
--- a/pyecsca/codegen/client.py
+++ b/pyecsca/codegen/client.py
@@ -1,13 +1,16 @@
#!/usr/bin/env python3
import subprocess
-from binascii import hexlify
+from binascii import hexlify, unhexlify
from os import path
from subprocess import Popen
-from typing import Mapping, Union, Optional
+from typing import Mapping, Union, Optional, Tuple
import click
+from public import public
+from pyecsca.ec.coordinates import CoordinateModel, AffineCoordinateModel
from pyecsca.ec.curves import get_params
from pyecsca.ec.mod import Mod
+from pyecsca.ec.model import CurveModel
from pyecsca.ec.params import DomainParameters
from pyecsca.ec.point import Point, InfinityPoint
from pyecsca.sca import SerialTarget
@@ -20,6 +23,7 @@ def encode_scalar(val: Union[int, Mod]) -> bytes:
return val.to_bytes((val.bit_length() + 7) // 8, "big")
elif isinstance(val, Mod):
return encode_scalar(int(val))
+ return bytes()
def encode_point(point: Point) -> Mapping:
@@ -30,6 +34,8 @@ def encode_point(point: Point) -> Mapping:
def encode_data(name: Optional[str], structure: Union[Mapping, bytes]) -> bytes:
if isinstance(structure, bytes):
+ if name is None:
+ raise ValueError
header = bytes([ord(name)]) + bytes([len(structure)])
return header + structure
data = bytes()
@@ -56,11 +62,13 @@ def decode_data(data: bytes) -> Mapping:
return result
+@public
def cmd_init_prng(seed: bytes) -> str:
return "i" + hexlify(seed).decode()
-def cmd_set_curve(group: DomainParameters) -> str:
+@public
+def cmd_set_params(group: DomainParameters) -> str:
data = {
"p": encode_scalar(group.curve.prime),
"n": encode_scalar(group.order),
@@ -73,43 +81,137 @@ def cmd_set_curve(group: DomainParameters) -> str:
return "c" + hexlify(encode_data(None, data)).decode()
+@public
def cmd_generate() -> str:
return "g"
+@public
def cmd_set_privkey(privkey: int) -> str:
return "s" + hexlify(encode_data(None, {"s": encode_scalar(privkey)})).decode()
+@public
def cmd_set_pubkey(pubkey: Point) -> str:
return "w" + hexlify(encode_data(None, {"w": encode_point(pubkey.to_affine())})).decode()
+@public
def cmd_scalar_mult(scalar: int) -> str:
return "m" + hexlify(encode_data(None, {"s": encode_scalar(scalar)})).decode()
+@public
def cmd_ecdh(pubkey: Point) -> str:
return "e" + hexlify(encode_data(None, {"w": encode_point(pubkey.to_affine())})).decode()
+@public
def cmd_ecdsa_sign(data: bytes) -> str:
return "a" + hexlify(encode_data(None, {"d": data})).decode()
+@public
def cmd_ecdsa_verify(data: bytes, sig: bytes) -> str:
return "v" + hexlify(encode_data(None, {"d": data, "s": sig})).decode()
+@public
def cmd_debug() -> str:
return "d"
-class BinaryTarget(SerialTarget):
+class ImplTarget(SerialTarget):
+ model: CurveModel
+ coords: CoordinateModel
+ seed: Optional[bytes]
+ params: Optional[DomainParameters]
+ privkey: Optional[int]
+ pubkey: Optional[Point]
+
+ def __init__(self, model: CurveModel, coords: CoordinateModel):
+ self.model = model
+ self.coords = coords
+ self.seed = None
+ self.params = None
+ self.privkey = None
+ self.pubkey = None
+
+ def init_prng(self, seed: bytes) -> None:
+ self.write(cmd_init_prng(seed).encode())
+ self.read(1)
+ self.seed = seed
+
+ def set_params(self, params: DomainParameters) -> None:
+ self.write(cmd_set_params(params).encode())
+ self.read(1)
+ self.params = params
+
+ def generate(self) -> Tuple[int, Point]:
+ self.write(cmd_generate().encode())
+ priv = self.read(1).decode()[1:-1]
+ pub = self.read(1).decode()[1:-1]
+ self.read(1)
+ self.privkey = int(priv, 16)
+ pub_len = len(pub)
+ x = int(pub[:pub_len // 2], 16)
+ y = int(pub[pub_len // 2:], 16)
+ self.pubkey = Point(AffineCoordinateModel(self.model), x=Mod(x, self.params.curve.prime),
+ y=Mod(y, self.params.curve.prime))
+ return self.privkey, self.pubkey
+
+ def set_privkey(self, privkey: int) -> None:
+ self.write(cmd_set_privkey(privkey).encode())
+ self.read(1)
+ self.privkey = privkey
+
+ def set_pubkey(self, pubkey: Point) -> None:
+ self.write(cmd_set_pubkey(pubkey).encode())
+ self.read(1)
+ self.pubkey = pubkey
+
+ def scalar_mult(self, scalar: int) -> Point:
+ self.write(cmd_scalar_mult(scalar).encode())
+ result = self.read(1)[1:-1]
+ plen = (self.params.curve.prime + 7) // 8
+ self.read(1)
+ params = {var: Mod(int(result[i * plen:(i + 1) * plen], 16), self.params.curve.prime) for
+ i, var in enumerate(self.coords.variables)}
+ return Point(self.coords, **params)
+
+ def ecdh(self, other_pubkey: Point) -> bytes:
+ self.write(cmd_ecdh(other_pubkey).encode())
+ result = self.read(1)
+ self.read(1)
+ return unhexlify(result[1:-1])
+
+ def ecdsa_sign(self, data: bytes) -> bytes:
+ self.write(cmd_ecdsa_sign(data).encode())
+ signature = self.read(1)
+ self.read(1)
+ return unhexlify(signature[1:-1])
+
+ def ecdsa_verify(self, data: bytes, signature: bytes) -> bool:
+ self.write(cmd_ecdsa_verify(data, signature).encode())
+ result = self.read(1)
+ self.read(1)
+ return unhexlify(result[1:-1])[0] == 1
+
+ def debug(self) -> Tuple[str, str]:
+ self.write(cmd_debug().encode())
+ resp = self.read(1)
+ self.read(1)
+ model, coords = unhexlify(resp[1:-1]).decode().split(",")
+ return model, coords
+
+
+@public
+class BinaryTarget(ImplTarget):
binary: str
process: Optional[Popen]
- def __init__(self, binary: str):
+ def __init__(self, binary: str, model: CurveModel, coords: CoordinateModel):
+ super().__init__(model, coords)
self.binary = binary
def connect(self):
@@ -117,15 +219,22 @@ class BinaryTarget(SerialTarget):
text=True, bufsize=1)
def write(self, data: bytes):
+ if self.process is None:
+ raise ValueError
self.process.stdin.write(data.decode() + "\n")
self.process.stdin.flush()
def read(self, timeout: int) -> bytes:
- return self.process.stdout.readline()
+ if self.process is None:
+ raise ValueError
+ return self.process.stdout.readline().encode()
def disconnect(self):
- if self.process.poll() is not None:
- self.process.terminate()
+ if self.process is None:
+ return
+ self.process.stdin.close()
+ self.process.stdout.close()
+ self.process.kill()
@click.group(context_settings={"help_option_names": ["-h", "--help"]})
@@ -134,9 +243,15 @@ class BinaryTarget(SerialTarget):
callback=wrap_enum(Platform),
help="The target platform to use.")
@click.option("--binary", help="For HOST target only. The binary to run.")
+@click.argument("model", required=True,
+ type=click.Choice(["shortw", "montgom", "edwards", "twisted"]),
+ callback=get_model)
+@click.argument("coords", required=True,
+ callback=get_coords)
@click.version_option()
@click.pass_context
-def main(ctx, platform, binary):
+@public
+def main(ctx, platform, binary, model, coords):
"""
A tool for communicating with built and flashed ECC implementations.
"""
@@ -158,40 +273,7 @@ def main(ctx, platform, binary):
if binary is None or not path.isfile(binary):
click.secho("Binary is required if the target is the host.", fg="red", err=True)
raise click.Abort
- ctx.obj["target"] = BinaryTarget(binary)
- # 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))
- # mul = LTRMultiplier(coords.formulas["add-1998-cmo"], coords.formulas["dbl-1998-cmo"], coords.formulas["z"])
- # mul.init(group, g)
- # res = mul.multiply(0x2EF035DF6D0634C7422161D08BCC794B5312E042DDB32B0135A4DE6E6345A555)
- # rx = Mod(0x77E3FF34C12571970845CBEB1BE0A79E3ECEE187510C2B8894BA800F8164C954, p)
- # ry = Mod(0x408A6A05607F9ACA97BB9A34EA643B107AADE0C9BB5EDB930EADE3009666B9D1, p)
- # rz = Mod(0xC66ECD687C335D63A7030434CA70351191BAFF1C206332EFEA39FA3003E91646, p)
- #
- # ox = Mod(0x3B1E7733E3250C97EB9D00AE0394F0768902DD337FEAAF7C4F6B9588462920DD, p)
- # oy = Mod(0xBA718497596C964E77F9666506505B1E730EE69D254E85AD44727DFFB2C7063E, p)
- # oz = Mod(0x0000000000000000000000000000000000000000000000000000000000000001, p)
- #
- # pt = Point(coords, X=rx, Y=ry, Z=rz)
- # ot = Point(coords, X=ox, Y=oy, Z=oz)
- # print(ot, res)
- # print(pt.equals(res))
- # print(ot.equals(res))
- # print(ot == res)
+ ctx.obj["target"] = BinaryTarget(binary, model, coords)
def get_curve(ctx: click.Context, param, value: Optional[str]) -> DomainParameters:
@@ -204,37 +286,28 @@ def get_curve(ctx: click.Context, param, value: Optional[str]) -> DomainParamete
@main.command("gen")
-@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("curve", required=True, callback=get_curve)
@click.pass_context
-def generate(ctx: click.Context, model, coords, curve):
+@public
+def generate(ctx: click.Context, curve):
ctx.ensure_object(dict)
- set_curve = cmd_set_curve(curve)
- generate = cmd_generate()
- target: SerialTarget = ctx.obj["target"]
+ target: ImplTarget = ctx.obj["target"]
target.connect()
- click.echo(set_curve)
- target.write(set_curve.encode())
- click.echo(target.read(1))
- click.echo(generate)
- target.write(generate.encode())
- click.echo(target.read(1))
- click.echo(target.read(1))
+ target.set_params(curve)
+ click.echo(target.generate())
target.disconnect()
@main.command("ecdh")
@click.pass_context
+@public
def ecdh(ctx: click.Context):
ctx.ensure_object(dict)
@main.command("ecdsa")
@click.pass_context
+@public
def ecdsa(ctx: click.Context):
ctx.ensure_object(dict)
diff --git a/pyecsca/codegen/common.py b/pyecsca/codegen/common.py
index bd57ab6..917963e 100644
--- a/pyecsca/codegen/common.py
+++ b/pyecsca/codegen/common.py
@@ -105,4 +105,4 @@ def get_coords(ctx: click.Context, param, value: Optional[str]) -> Optional[Coor
model.__class__.__name__))
coords = model.coordinates[value]
ctx.obj["coords"] = coords
- return coords \ No newline at end of file
+ return coords
diff --git a/pyecsca/codegen/point.h b/pyecsca/codegen/point.h
index 1b7e8e1..f85a818 100644
--- a/pyecsca/codegen/point.h
+++ b/pyecsca/codegen/point.h
@@ -13,9 +13,11 @@ void point_free(point_t *point);
bool point_equals(const point_t *one, const point_t *other);
-void point_to_affine(point_t *point, curve_t *curve, bn_t *out_x, bn_t *out_y);
+bool point_equals_affine(const point_t *one, const point_t *other, const curve_t *curve);
-void point_from_affine(bn_t *x, bn_t *y, curve_t *curve, point_t *out);
+void point_to_affine(const point_t *point, const curve_t *curve, bn_t *out_x, bn_t *out_y);
+
+void point_from_affine(bn_t *x, bn_t *y, const curve_t *curve, point_t *out);
void point_add(const point_t *one, const point_t *other, const curve_t *curve, point_t *out_one);
diff --git a/pyecsca/codegen/render.py b/pyecsca/codegen/render.py
index ead1470..6ea1e36 100644
--- a/pyecsca/codegen/render.py
+++ b/pyecsca/codegen/render.py
@@ -26,9 +26,9 @@ env = Environment(
loader=PackageLoader("pyecsca.codegen")
)
-
env.globals["isinstance"] = isinstance
+
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)
@@ -48,8 +48,10 @@ def render_op(op: OpType, result: str, left: str, right: str, mod: str) -> Optio
print(op, result, left, right, mod)
return None
+
env.globals["render_op"] = render_op
+
def render_defs(model: CurveModel, coords: CoordinateModel) -> str:
return env.get_template("defs.h").render(params=model.parameter_names,
variables=coords.variables)
@@ -233,7 +235,7 @@ def render(config: DeviceConfiguration) -> Tuple[str, str, str]:
@public
def build(dir: str, elf_file: str, hex_file: str, outdir: str, strip: bool = False,
- remove : bool = True) -> subprocess.CompletedProcess:
+ remove: bool = True) -> subprocess.CompletedProcess:
"""
:param dir:
@@ -248,7 +250,8 @@ def build(dir: str, elf_file: str, hex_file: str, outdir: str, strip: bool = Fal
if res.returncode != 0:
raise ValueError("Build failed!")
if strip:
- subprocess.run(["strip", elf_file], cwd=dir)
+ s = subprocess.run(["strip", elf_file], cwd=dir)
+ s.returncode
full_elf_path = path.join(dir, elf_file)
full_hex_path = path.join(dir, hex_file)
shutil.copy(full_elf_path, outdir)
@@ -271,4 +274,4 @@ def render_and_build(config: DeviceConfiguration, outdir: str, strip: bool = Fal
"""
dir, elf_file, hex_file = render(config)
res = build(dir, elf_file, hex_file, outdir, strip, remove)
- return dir, elf_file, hex_file, res \ No newline at end of file
+ return dir, elf_file, hex_file, res
diff --git a/pyecsca/codegen/templates/main.c b/pyecsca/codegen/templates/main.c
index c2e250f..ab69858 100644
--- a/pyecsca/codegen/templates/main.c
+++ b/pyecsca/codegen/templates/main.c
@@ -56,7 +56,7 @@ static uint8_t cmd_init_prng(uint8_t *data, uint16_t len) {
return 0;
}
-static void parse_set_curve(const char *path, const uint8_t *data, size_t len, void *arg) {
+static void parse_set_params(const char *path, const uint8_t *data, size_t len, void *arg) {
{%- for param in curve_parameters + ["p", "n", "h"] %}
if (strcmp(path, "{{ param }}") == 0) {
bn_from_bin(data, len, &curve->{{ param }});
@@ -90,10 +90,10 @@ static void parse_set_curve(const char *path, const uint8_t *data, size_t len, v
{%- endfor %}
}
-static uint8_t cmd_set_curve(uint8_t *data, uint16_t len) {
+static uint8_t cmd_set_params(uint8_t *data, uint16_t len) {
// need p, [params], n, h, g[xy], i[variables]
fat_t affine[2] = {fat_empty, fat_empty};
- parse_data(data, len, "", parse_set_curve, (void *) affine);
+ parse_data(data, len, "", parse_set_params, (void *) affine);
bn_t x; bn_init(&x);
bn_t y; bn_init(&y);
bn_from_bin(affine[0].value, affine[0].len, &x);
@@ -416,10 +416,10 @@ static uint8_t cmd_ecdsa_verify(uint8_t *data, uint16_t len) {
}
static uint8_t cmd_debug(uint8_t *data, uint16_t len) {
- const char *debug_string = "{{ ','.join((model.shortname, coords.name))}}";
+ char *debug_string = "{{ ','.join((model.shortname, coords.name))}}";
size_t debug_len = strlen(debug_string);
- simpleserial_put('d', debug_len, debug_string);
+ simpleserial_put('d', debug_len, (uint8_t *) debug_string);
return 0;
}
@@ -435,7 +435,7 @@ int main(void) {
simpleserial_init();
simpleserial_addcmd('i', MAX_SS_LEN, cmd_init_prng);
- simpleserial_addcmd('c', MAX_SS_LEN, cmd_set_curve);
+ simpleserial_addcmd('c', MAX_SS_LEN, cmd_set_params);
{%- if keygen %}
simpleserial_addcmd('g', 0, cmd_generate);
{%- endif %}
diff --git a/pyecsca/codegen/templates/mult.c b/pyecsca/codegen/templates/mult.c
index 61ec825..6851b6b 100644
--- a/pyecsca/codegen/templates/mult.c
+++ b/pyecsca/codegen/templates/mult.c
@@ -2,17 +2,17 @@
#include "mult.h"
{%- if isinstance(scalarmult, LTRMultiplier) -%}
-{%- include "mult_ltr.c" %}
+{% include "mult_ltr.c" %}
{%- elif isinstance(scalarmult, RTLMultiplier) -%}
-{%- include "mult_rtl.c" %}
+{% include "mult_rtl.c" %}
{%- elif isinstance(scalarmult, CoronMultiplier) -%}
-{%- include "mult_coron.c" %}
+{% include "mult_coron.c" %}
{%- elif isinstance(scalarmult, LadderMultiplier) -%}
-{%- include "mult_ldr.c" %}
+{% include "mult_ldr.c" %}
{%- elif isinstance(scalarmult, SimpleLadderMultiplier) -%}
-{%- include "mult_simple_ldr.c" %}
+{% include "mult_simple_ldr.c" %}
{%- elif isinstance(scalarmult, DifferentialLadderMultiplier) -%}
-{%- include "mult_diff_ldr.c" %}
+{% include "mult_diff_ldr.c" %}
{%- elif isinstance(scalarmult, BinaryNAFMultiplier) -%}
-{%- include "mult_bnaf.c" %}
+{% include "mult_bnaf.c" %}
{%- endif -%}
diff --git a/pyecsca/codegen/templates/mult_ltr.c b/pyecsca/codegen/templates/mult_ltr.c
index 9b7d87e..dbd4704 100644
--- a/pyecsca/codegen/templates/mult_ltr.c
+++ b/pyecsca/codegen/templates/mult_ltr.c
@@ -7,8 +7,7 @@ void scalar_mult(bn_t *scalar, point_t *point, curve_t *curve, point_t *out) {
point_t *r = point_copy(curve->neutral);
int nbits = bn_bit_length(&curve->n) - 1;
{%- else %}
- point_t *q = point_new();
- point_dbl(point, curve, q);
+ point_t *q = point_copy(point);
point_t *r = point_copy(point);
int nbits = bn_bit_length(scalar) - 2;
{%- endif %}
diff --git a/pyecsca/codegen/templates/point.c b/pyecsca/codegen/templates/point.c
index c12c50a..445bdc4 100644
--- a/pyecsca/codegen/templates/point.c
+++ b/pyecsca/codegen/templates/point.c
@@ -20,6 +20,7 @@ void point_set(const point_t *from, point_t *out) {
{%- for variable in variables %}
bn_copy(&from->{{ variable }}, &out->{{ variable }});
{%- endfor %}
+ out->infinity = from->infinity;
}
void point_free(point_t *point) {
@@ -30,7 +31,7 @@ void point_free(point_t *point) {
}
bool point_equals(const point_t *one, const point_t *other) {
- if (one->infinity && !other->infinity || other->infinity && !one->infinity) {
+ if ((one->infinity && !other->infinity) || (other->infinity && !one->infinity)) {
return false;
}
if (one->infinity && other->infinity) {
@@ -45,7 +46,34 @@ bool point_equals(const point_t *one, const point_t *other) {
return true;
}
-void point_to_affine(point_t *point, curve_t *curve, bn_t *out_x, bn_t *out_y) {
+bool point_equals_affine(const point_t *one, const point_t *other, const curve_t *curve) {
+ if ((one->infinity && !other->infinity) || (other->infinity && !one->infinity)) {
+ return false;
+ }
+ if (one->infinity && other->infinity) {
+ return true;
+ }
+ bool result = true;
+ bn_t ax; bn_init(&ax);
+ bn_t ay; bn_init(&ay);
+ bn_t bx; bn_init(&bx);
+ bn_t by; bn_init(&by);
+ point_to_affine(one, curve, &ax, &ay);
+ point_to_affine(other, curve, &bx, &by);
+ if (!bn_eq(&ax, &bx)) {
+ result = false;
+ }
+ if (!bn_eq(&ay, &by)) {
+ result = false;
+ }
+ bn_clear(&ax);
+ bn_clear(&ay);
+ bn_clear(&bx);
+ bn_clear(&by);
+ return result;
+}
+
+void point_to_affine(const point_t *point, const curve_t *curve, bn_t *out_x, bn_t *out_y) {
{%- include "ops.c" %}
{%- if "x" in allocations %}
if (out_x) {
@@ -62,7 +90,7 @@ void point_to_affine(point_t *point, curve_t *curve, bn_t *out_x, bn_t *out_y) {
{%- endfor %}
}
-void point_from_affine(bn_t *x, bn_t *y, curve_t *curve, point_t *out) {
+void point_from_affine(bn_t *x, bn_t *y, const curve_t *curve, point_t *out) {
{# XXX: This just works for the stuff currently in EFD. #}
{%- for variable in variables %}
{%- if variable in ("X", "Y") %}
diff --git a/test/test_builder.py b/test/test_builder.py
index a16f714..3d1651a 100644
--- a/test/test_builder.py
+++ b/test/test_builder.py
@@ -1,38 +1,12 @@
from unittest import TestCase
-import tempfile
from click.testing import CliRunner
-from pyecsca.ec.curves import get_params
-from pyecsca.ec.mult import LTRMultiplier
-from pyecsca.ec.configuration import HashType, RandomMod, Multiplication, Squaring, Reduction
-from pyecsca.codegen.common import Platform, DeviceConfiguration
-from pyecsca.codegen.render import render_and_build
from pyecsca.codegen.builder import build_impl, list_impl
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
- params = get_params("secg", "secp128r1", "projective")
- model = params.curve.model
- coords = params.curve.coordinate_model
- 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 = DeviceConfiguration(model, coords, formulas, scalarmult, hash_type, mod_rand, mult,
- sqr, red, platform, True, True, True)
- temp = tempfile.mkdtemp()
- render_and_build(config, temp, True)
-
def test_cli_build(self):
runner = CliRunner()
with runner.isolated_filesystem():
diff --git a/test/test_client.py b/test/test_client.py
index 93f7a82..f854a5c 100644
--- a/test/test_client.py
+++ b/test/test_client.py
@@ -1,3 +1,4 @@
+from os.path import join
from unittest import TestCase
from click.testing import CliRunner
@@ -6,7 +7,8 @@ from pyecsca.ec.mod import Mod
from pyecsca.codegen.builder import build_impl
from pyecsca.codegen.client import (encode_data, decode_data, encode_scalar, cmd_init_prng,
- cmd_set_curve, cmd_set_pubkey, cmd_set_privkey, cmd_scalar_mult,
+ cmd_set_params, cmd_set_pubkey, cmd_set_privkey,
+ cmd_scalar_mult,
cmd_ecdh, cmd_ecdsa_sign, cmd_ecdsa_verify, cmd_generate,
cmd_debug, main)
@@ -35,7 +37,7 @@ class CommandTest(TestCase):
cmd_init_prng(bytes([0xca, 0xfe, 0xba, 0xbe]))
def test_set_curve(self):
- cmd_set_curve(self.curve)
+ cmd_set_params(self.curve)
def test_generate(self):
cmd_generate()
@@ -66,11 +68,12 @@ class ClientTests(TestCase):
def test_generate(self):
runner = CliRunner()
- with runner.isolated_filesystem():
+ with runner.isolated_filesystem() as tmpdir:
runner.invoke(build_impl,
["--platform", "HOST", "-v", "shortw", "projective",
"add-1998-cmo", "dbl-1998-cmo", "z", "ltr(complete=False)", "."])
result = runner.invoke(main,
- ["--platform", "HOST", "--binary", "./pyecsca-codegen-HOST.elf",
- "gen", "shortw", "projective", "secg/secp128r1"])
+ ["--platform", "HOST", "--binary",
+ join(tmpdir, "pyecsca-codegen-HOST.elf"),
+ "shortw", "projective", "gen", "secg/secp128r1"])
self.assertEqual(result.exit_code, 0)
diff --git a/test/test_impl.py b/test/test_impl.py
new file mode 100644
index 0000000..d93501d
--- /dev/null
+++ b/test/test_impl.py
@@ -0,0 +1,77 @@
+from os.path import join
+from unittest import TestCase
+
+from click.testing import CliRunner
+from pyecsca.ec.curves import get_params
+from pyecsca.ec.mult import LTRMultiplier, RTLMultiplier, CoronMultiplier
+
+from pyecsca.codegen.builder import build_impl
+from pyecsca.codegen.client import BinaryTarget
+
+
+class ImplementationTest(TestCase):
+
+ def setUp(self):
+ self.secp128r1 = get_params("secg", "secp128r1", "projective")
+ self.base = self.secp128r1.generator
+ self.coords = self.secp128r1.curve.coordinate_model
+
+ self.curve25519 = get_params("other", "Curve25519", "xz")
+ self.base25519 = self.curve25519.generator
+ self.coords25519 = self.curve25519.curve.coordinate_model
+
+ def do_basic_test(self, runner, params, mult_class, formulas, mult_name, **mult_kwargs):
+ with runner.isolated_filesystem() as tmpdir:
+ runner.invoke(build_impl,
+ ["--platform", "HOST", "--no-ecdsa", "--no-ecdh",
+ params.curve.model.shortname, params.curve.coordinate_model.name,
+ *formulas,
+ f"{mult_name}({','.join(f'{key}={value}' for key, value in mult_kwargs.items())})",
+ "."])
+ target = BinaryTarget(join(tmpdir, "pyecsca-codegen-HOST.elf"),
+ params.curve.model,
+ params.curve.coordinate_model)
+ target.connect()
+ target.set_params(params)
+ priv, pub = target.generate()
+ self.assertTrue(params.curve.is_on_curve(pub))
+ formula_instances = [params.curve.coordinate_model.formulas[formula] for formula
+ in formulas]
+ mult = mult_class(*formula_instances, **mult_kwargs)
+ mult.init(params, params.generator)
+ expected = mult.multiply(priv).to_affine()
+ self.assertEqual(pub, expected)
+ target.disconnect()
+
+ def test_ltr(self):
+ runner = CliRunner()
+ self.do_basic_test(runner, self.secp128r1, LTRMultiplier, ["add-1998-cmo", "dbl-1998-cmo"],
+ "ltr",
+ complete=False)
+ self.do_basic_test(runner, self.secp128r1, LTRMultiplier, ["add-1998-cmo", "dbl-1998-cmo"],
+ "ltr",
+ complete=True)
+ self.do_basic_test(runner, self.secp128r1, LTRMultiplier, ["add-1998-cmo", "dbl-1998-cmo"],
+ "ltr",
+ always=True, complete=False)
+ self.do_basic_test(runner, self.secp128r1, LTRMultiplier, ["add-1998-cmo", "dbl-1998-cmo"],
+ "ltr",
+ always=True, complete=True)
+
+ def test_rtl(self):
+ runner = CliRunner()
+ self.do_basic_test(runner, self.secp128r1, RTLMultiplier, ["add-1998-cmo", "dbl-1998-cmo"],
+ "rtl",
+ always=False)
+ self.do_basic_test(runner, self.secp128r1, RTLMultiplier, ["add-1998-cmo", "dbl-1998-cmo"],
+ "rtl",
+ always=True)
+
+ def test_coron(self):
+ runner = CliRunner()
+ self.do_basic_test(runner, self.secp128r1, CoronMultiplier,
+ ["add-1998-cmo", "dbl-1998-cmo"], "coron")
+
+ # def test_ladder(self):
+ # runner = CliRunner()
+ # self.do_basic_test(runner, self.curve25519, LadderMultiplier, ["ladd-1987-m", "dbl-1987-m"], "ldr")
diff --git a/test/test_render.py b/test/test_render.py
new file mode 100644
index 0000000..eefa626
--- /dev/null
+++ b/test/test_render.py
@@ -0,0 +1,32 @@
+import tempfile
+from unittest import TestCase
+
+from pyecsca.ec.configuration import HashType, RandomMod, Multiplication, Squaring, Reduction
+from pyecsca.ec.curves import get_params
+from pyecsca.ec.mult import LTRMultiplier
+
+from pyecsca.codegen.common import Platform, DeviceConfiguration
+from pyecsca.codegen.render import render_and_build
+
+
+class RenderTests(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
+ params = get_params("secg", "secp128r1", "projective")
+ model = params.curve.model
+ coords = params.curve.coordinate_model
+ 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 = DeviceConfiguration(model, coords, formulas, scalarmult, hash_type, mod_rand, mult,
+ sqr, red, platform, True, True, True)
+ temp = tempfile.mkdtemp()
+ render_and_build(config, temp, True)