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))