Code generation

pyecsca can generate C implementations of ECC crypto for several microprocessor targets, which are defined by the Platform enum.

[ ]:
from pyecsca.codegen.common import Platform


Platform.names()

To generate an implementation we need an actual configuration which the implementation should implement, which is stored inside the Configuration and DeviceConfiguration classes.

[ ]:
from typing import get_args
from pyecsca.codegen.common import DeviceConfiguration
from dataclasses import fields

for field in fields(DeviceConfiguration):
    name = field.name
    tp = field.type
    doc = tp.__doc__
    if get_args(field.type):
            doc = get_args(field.type)[0].__doc__
    if tp == bool:
            doc = ""
    print(name, tp)
    print("   ", doc)
    if hasattr(tp, "names"):
            for enum_name in tp.names():
                    print("       ", enum_name)
    print()
()

The DeviceConfiguration class contains a few additional attributes apart from those in the Configuration class: platform, keygen, ecdh and ecdsa.

The platform attribute defines for which target the implementation should be built. The other boolean attributes specify whether particular functionality should be implemented and enabled in the implementation.

Generating

We will first create a DeviceConfiguration, which we will then generate and build.

[ ]:
from pyecsca.ec.model import ShortWeierstrassModel
from pyecsca.ec.mult import LTRMultiplier
from pyecsca.ec.configuration import *

platform = Platform.STM32F3
hash_type = HashType.SHA1
mod_rand = RandomMod.REDUCE
mult = Multiplication.BASE
sqr = Squaring.BASE
red = Reduction.BASE
inv = Inversion.GCD

model = ShortWeierstrassModel()
coords = model.coordinates["projective"]
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,
                                                     inv, platform, True, True, True)

config

Now we can render the configuration, which will generate the source files into a randomly created temporary directory, and return the path to the directory as well as names of the elf and hex files which will be built in that directory.

[ ]:
from pyecsca.codegen.builder import render

directory, elf_name, hex_name = render(config)

print(directory)

Building

When we have the implementation rendered, we can build it using make.

[ ]:
from subprocess import run

res = run(["make"], cwd=directory, capture_output=True)
print(res.stdout.decode())

Now the files elf_name and hex_name in the directory contain the ELF file and HEX file built.

[ ]:
res = run(["file", elf_name], cwd=directory, capture_output=True)
print(res.stdout.decode())

res = run(["file", hex_name], cwd=directory, capture_output=True)
print(res.stdout.decode())

Running

We will now run key generation using the generated implementation on the STM32F3 target.

[ ]:
from pyecsca.codegen.client import DeviceTarget
from pyecsca.ec.params import get_params

params = get_params("secg", "secp128r1", "projective")
target = DeviceTarget(params.curve.model, params.curve.coordinate_model, Platform.STM32F3, timeout=10000)

Flash the implementation.

[ ]:
from os.path import join

target.flash(join(directory, hex_name))

Run the key generation.

[ ]:
target.connect()
target.set_params(params)
priv, pub = target.generate()
target.disconnect()

And we can check that the generated keypair is valid.

[ ]:
print(params.curve.is_on_curve(pub))

print(priv)
print(pub)
print(params.curve.affine_multiply(params.generator.to_affine(), priv))

Running on the host

We can also run the implementation on the host.

[ ]:
config = DeviceConfiguration(model, coords, formulas, scalarmult,
                                                     hash_type, mod_rand, mult, sqr, red,
                                                     inv, Platform.HOST, True, True, True)

directory, elf_name, hex_name = render(config)
res = run(["make"], cwd=directory, capture_output=True)
print(res.stdout.decode())
[ ]:
from pyecsca.codegen.client import HostTarget
from os.path import join

target = HostTarget(params.curve.model, params.curve.coordinate_model, binary=join(directory, elf_name))
[ ]:
target.connect()
target.set_params(params)
priv, pub = target.generate()
target.disconnect()
[ ]:
print(params.curve.is_on_curve(pub))

print(priv)
print(pub)
print(params.curve.affine_multiply(params.generator.to_affine(), priv))