Source code for pyecsca.codegen.client

#!/usr/bin/env python3
from binascii import hexlify
from typing import Mapping, Union, Optional, Any
from os import path

import click
from pyecsca.ec.params import DomainParameters
from pyecsca.ec.mod import Mod
from pyecsca.ec.point import Point

from .common import EnumDefine, wrap_enum

try:
    import chipwhisperer as cw
except ImportError:
    cw = None


[docs]class Target(EnumDefine): HOST = "HOST" SIMPLESERIAL = "CWLITE_SIMPLESERIAL" JCARD = "CWLITE_JCARD"
[docs]def encode_scalar(val: Union[int, Mod]) -> bytes: if isinstance(val, int): return val.to_bytes((val.bit_length() + 7) // 8, "big") elif isinstance(val, Mod): return encode_scalar(val.x)
[docs]def encode_point(point: Point) -> Mapping: return {var: encode_scalar(value) for var, value in point.coords.items()}
[docs]def encode_data(name: Optional[str], structure: Union[Mapping, bytes]) -> bytes: if isinstance(structure, bytes): header = bytes([ord(name)]) + bytes([len(structure)]) return header + structure data = bytes() for k, v in structure.items(): data += encode_data(k, v) if name is not None: return bytes([ord(name) | 0x80]) + bytes([len(data)]) + data return data
[docs]def decode_data(data: bytes) -> Mapping: result = {} parsed = 0 while parsed < len(data): name = data[parsed] length = data[parsed + 1] if name & 0x80: sub = decode_data(data[parsed + 2: parsed + 2 + length]) result[chr(name | 0x7f)] = sub parsed += len(sub) + 2 else: result[chr(name)] = data[parsed + 2: parsed + 2 + length] parsed += length + 2 return result
[docs]def cmd_init_prng(seed: bytes) -> str: return "i" + hexlify(seed).decode()
[docs]def cmd_set_curve(group: DomainParameters) -> str: data = { "p": encode_scalar(group.curve.prime), "n": encode_scalar(group.order), "h": encode_scalar(group.cofactor) } for param, value in group.curve.parameters.items(): data[param] = encode_scalar(value) data["g"] = encode_point(group.generator) data["i"] = encode_point(group.neutral) return "c" + hexlify(encode_data(None, data)).decode()
[docs]def cmd_generate() -> str: return "g"
[docs]def cmd_set_privkey(privkey: int) -> str: return "s" + hexlify(encode_data(None, {"s": encode_scalar(privkey)})).decode()
[docs]def cmd_set_pubkey(pubkey: Point) -> str: return "w" + hexlify(encode_data(None, {"w": encode_point(pubkey)})).decode()
[docs]def cmd_scalar_mult(scalar: int) -> str: return "m" + hexlify(encode_data(None, {"s": encode_scalar(scalar)})).decode()
[docs]def cmd_ecdh(pubkey: Point) -> str: return "e" + hexlify(encode_data(None, {"w": encode_point(pubkey)})).decode()
[docs]def cmd_ecdsa_sign(data: bytes) -> str: return "a" + hexlify(encode_data(None, {"d": data})).decode()
[docs]def cmd_ecdsa_verify(data: bytes, sig: bytes) -> str: return "v" + hexlify(encode_data(None, {"d": data, "s": sig})).decode()
[docs]def setup_scope(target_type: Target): if target_type in (Target.SIMPLESERIAL, Target.JCARD): scope = cw.scope() scope.default_setup() return scope return None
[docs]def connect(target_type: Target, scope, **kwargs) -> Any: if target_type == Target.HOST: return kwargs["binary"] elif target_type == Target.SIMPLESERIAL: return cw.target(scope, cw.targets.SimpleSerial) elif target_type == Target.JCARD: return None # TODO: this
[docs]def send_command(target_type: Target, target: Any, command: str) -> str: if target_type == Target.SIMPLESERIAL: target.write(command + "\n") result = target.read() target.simpleserial_wait_ack() return result return None # TODO: this
@click.group(context_settings={"help_option_names": ["-h", "--help"]}) @click.option("--target", envvar="TARGET", required=True, type=click.Choice(Target.names()), callback=wrap_enum(Target), help="The target to use.") @click.option("--binary", help="For HOST target only. The binary to run.") @click.version_option() def main(target, binary): """ A tool for communicating with built and flashed ECC implementations. """ if target in (Target.SIMPLESERIAL, Target.JCARD) and cw is None: click.secho("ChipWhisperer not installed, SIMPLESERIAL and JCARD targets require it.", fg="red", err=True) raise click.Abort if target == Target.HOST and (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 # 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) @main.command("gen") def generate(): pass @main.command("ecdh") def ecdh(): pass @main.command("ecdsa") def ecdsa(): pass if __name__ == "__main__": main()