diff options
| author | J08nY | 2020-02-19 18:29:53 +0100 |
|---|---|---|
| committer | J08nY | 2020-02-19 18:29:53 +0100 |
| commit | 5da1d167c203395103d220c450e29fece08f4198 (patch) | |
| tree | fdc8fb7f0fbf2d2aaab46c2cb8a90a8176058292 | |
| parent | f04c640c05e9ffe894f67194832623e28a8000f5 (diff) | |
| download | pyecsca-codegen-5da1d167c203395103d220c450e29fece08f4198.tar.gz pyecsca-codegen-5da1d167c203395103d220c450e29fece08f4198.tar.zst pyecsca-codegen-5da1d167c203395103d220c450e29fece08f4198.zip | |
Flesh out client, add implementation tests.
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | pyecsca/codegen/builder.py | 23 | ||||
| -rw-r--r-- | pyecsca/codegen/client.py | 191 | ||||
| -rw-r--r-- | pyecsca/codegen/common.py | 2 | ||||
| -rw-r--r-- | pyecsca/codegen/point.h | 6 | ||||
| -rw-r--r-- | pyecsca/codegen/render.py | 11 | ||||
| -rw-r--r-- | pyecsca/codegen/templates/main.c | 12 | ||||
| -rw-r--r-- | pyecsca/codegen/templates/mult.c | 14 | ||||
| -rw-r--r-- | pyecsca/codegen/templates/mult_ltr.c | 3 | ||||
| -rw-r--r-- | pyecsca/codegen/templates/point.c | 34 | ||||
| -rw-r--r-- | test/test_builder.py | 26 | ||||
| -rw-r--r-- | test/test_client.py | 13 | ||||
| -rw-r--r-- | test/test_impl.py | 77 | ||||
| -rw-r--r-- | test/test_render.py | 32 |
14 files changed, 319 insertions, 127 deletions
@@ -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) |
