Configuration space¶
This notebook explores the configuration space of Elliptic Curve crypto implementations.
An ECC implementation configuration in pyecsca has the following attributes:
[ ]:
from typing import get_args
from pyecsca.ec.configuration import Configuration
from dataclasses import fields
for field in fields(Configuration):
name = field.name
tp = field.type
doc = tp.__doc__
if get_args(field.type):
doc = get_args(field.type)[0].__doc__
print(name, tp)
print(" ", doc)
if hasattr(tp, "names"):
for enum_name in tp.names():
print(" ", enum_name)
print()
It is represented by the Configuration
class.
Enumerating configurations¶
The possible configurations can be generated using the all_configurations()
function. The whole space of configurations is quite huge.
Note that the amount of possible parametrizations of some scalar multipliers is very large (e.g. window size) so pyecsca has to resctrict this to a reasonable selection. Specifically the function below only considers a few options for window size and similar parameters.
[ ]:
from pyecsca.ec.configuration import all_configurations
total = sum(1 for _ in all_configurations())
print(total)
A large part of the configuration space is due to the independent options which consist of:
hash_type
of typeHashType
\(*6\)mod_rand
of typeRandomMod
\(*2\)mult
of typeMultiplication
\(*4\)sqr
of typeSquaring
\(*4\)red
of typeReduction
\(*3\)inv
of typeInversion
\(*2\)
Without these, the space is somewhat smaller:
[ ]:
no_indep = (6*2*4*4*3*2)
no_ff = (6*2)
[ ]:
print(total // no_indep)
To restrict the generated configurations, pass keyword arguments to the all_configurations
matching the names of the attributes of the Configuration
object.
[ ]:
from pyecsca.ec.configuration import HashType, RandomMod, Multiplication, Squaring, Reduction, Inversion
from pyecsca.ec.model import ShortWeierstrassModel
from pyecsca.ec.mult import LTRMultiplier
model = ShortWeierstrassModel()
coords = model.coordinates["projective"]
scalarmult = LTRMultiplier
independent_opts = {
"hash_type": HashType.SHA256,
"mod_rand": RandomMod.SAMPLE,
"mult": Multiplication.KARATSUBA,
"sqr": Squaring.KARATSUBA,
"red": Reduction.MONTGOMERY,
"inv": Inversion.GCD
}
configs = list(all_configurations(model=model, coords=coords, scalarmult=scalarmult,
**independent_opts))
print(len(configs))
We see that when we fixed all parameters except for the scalar multiplier arguments (see the LTRMultiplier
constructor) we obtained 1280 configurations. That number expresses all of the possible ways to use addition formulas for the projective
coordinate system in the binary left-to-right multiplier as well as internal options of that multiplier:
whether it is “complete” in a sense that it starts processing at a constant bit (the bit-length od the order)
whether it is “double-and-add-always”
whether it “short-circuits” the formulas, i.e. detects that an exceptional point was input into them and returns correctly without executing them.
Models¶
We can explore the number of configurations for each of the supported curve models.
[ ]:
from IPython.display import HTML, display
import tabulate
from pyecsca.ec.model import *
from pyecsca.ec.mult import ProcessingDirection, AccumulationOrder
model_counts = [["Model", "All", "Without independent options", "Without independent options and scaling", "Without independent options and scalarmult options"]]
totals = ["Total", 0, 0, 0, 0]
for model in (ShortWeierstrassModel(), MontgomeryModel(), EdwardsModel(), TwistedEdwardsModel()):
name = model.__class__.__name__
count = sum(1 for _ in all_configurations(model=model, **independent_opts))
count_no_scl = sum(1 for _ in all_configurations(model=model, **independent_opts, scalarmult={"scl": None}))
count_no_opts = sum(1 for _ in all_configurations(model=model, **independent_opts, scalarmult={"scl": None, "always": True, "short_circuit": True, "complete": False, "precompute_negation": True, "width": 3, "m": 3, "direction": ProcessingDirection.LTR, "accumulation_order": AccumulationOrder.PeqPR, "recoding_direction": ProcessingDirection.LTR}))
model_counts.append([name, count * no_ff, count, count_no_scl, count_no_opts])
totals[1] += count * no_ff
totals[2] += count
totals[3] += count_no_scl
totals[4] += count_no_opts
model_counts.append(totals)
display(HTML(tabulate.tabulate(model_counts, tablefmt="html", headers="firstrow")))
Coordinate systems¶
Let’s now look at the configuration split for coordinate systems:
[ ]:
coords_counts = [["Model", "Coords", "All", "Without independent options", "Without independent options and scaling", "Without independent options and scalarmult options"]]
for model in (ShortWeierstrassModel(), MontgomeryModel(), EdwardsModel(), TwistedEdwardsModel()):
model_name = model.__class__.__name__
coords_counts.append([model_name, "", "", "", "", ""])
for coords in sorted(model.coordinates.values(), key=lambda c: c.name):
coords_name = coords.name
count = sum(1 for _ in all_configurations(model=model, coords=coords, **independent_opts))
count_no_scl = sum(1 for _ in all_configurations(model=model, coords=coords, **independent_opts, scalarmult={"scl": None}))
count_no_opts = sum(1 for _ in all_configurations(model=model, coords=coords, **independent_opts, scalarmult={"scl": None, "always": True, "short_circuit": True, "complete": False, "precompute_negation": True, "width": 3, "m": 3, "direction": ProcessingDirection.LTR, "accumulation_order": AccumulationOrder.PeqPR, "recoding_direction": ProcessingDirection.LTR}))
coords_counts.append(["", coords_name, count * no_ff, count, count_no_scl, count_no_opts])
display(HTML(tabulate.tabulate(coords_counts, tablefmt="html", headers="firstrow")))
Scalar multipliers¶
[ ]:
from pyecsca.ec.mult import ScalarMultiplier
def leaf_subclasses(cls):
subs = cls.__subclasses__()
result = set()
for subclass in subs:
if subclass.__subclasses__():
result.update(leaf_subclasses(subclass))
else:
result.add(subclass)
return result
mult_counts = [["ScalarMultiplier", "All", "Without independent options", "Without independent options and scaling", "Without independent options and scalarmult options"]]
for mult_cls in leaf_subclasses(ScalarMultiplier):
count = sum(1 for _ in all_configurations(**independent_opts, scalarmult=mult_cls))
count_no_scl = sum(1 for _ in all_configurations(**independent_opts, scalarmult={"cls": mult_cls, "scl": None}))
count_no_opts = sum(1 for _ in all_configurations(**independent_opts, scalarmult={"cls": mult_cls, "scl": None, "always": True, "short_circuit": True, "complete": False, "precompute_negation": True, "width": 3, "m": 3, "direction": ProcessingDirection.LTR, "accumulation_order": AccumulationOrder.PeqPR, "recoding_direction": ProcessingDirection.LTR}))
mult_counts.append([mult_cls.__name__, count * no_ff, count, count_no_scl, count_no_opts])
display(HTML(tabulate.tabulate(mult_counts, tablefmt="html", headers="firstrow")))
[ ]: