aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pyecsca/ec/curve.py6
-rw-r--r--pyecsca/ec/model.py68
-rw-r--r--pyecsca/ec/mult.py104
-rw-r--r--pyecsca/ec/naf.py30
-rw-r--r--pyecsca/sca/trace_set/inspector.py2
-rw-r--r--pyecsca/sca/ttest.py6
-rw-r--r--test/ec/test_model.py8
-rw-r--r--test/ec/test_mult.py19
-rw-r--r--test/ec/test_naf.py10
9 files changed, 198 insertions, 55 deletions
diff --git a/pyecsca/ec/curve.py b/pyecsca/ec/curve.py
index d4cef5d..bf2cb36 100644
--- a/pyecsca/ec/curve.py
+++ b/pyecsca/ec/curve.py
@@ -1,4 +1,4 @@
-from typing import Type, Mapping
+from typing import Mapping
from .point import Point
from .coordinates import CoordinateModel
@@ -6,12 +6,12 @@ from .model import CurveModel
class EllipticCurve(object):
- model: Type[CurveModel]
+ model: CurveModel
coordinate_model: CoordinateModel
parameters: Mapping[str, int]
neutral: Point
- def __init__(self, model: Type[CurveModel], coordinate_model: CoordinateModel,
+ def __init__(self, model: CurveModel, coordinate_model: CoordinateModel,
parameters: Mapping[str, int], neutral: Point):
if coordinate_model not in model.coordinates.values():
raise ValueError
diff --git a/pyecsca/ec/model.py b/pyecsca/ec/model.py
index 84928bc..749dbbf 100644
--- a/pyecsca/ec/model.py
+++ b/pyecsca/ec/model.py
@@ -8,6 +8,7 @@ from .coordinates import CoordinateModel
class CurveModel(object):
_efd_name: str
+ _loaded: bool = False
name: str
coordinates: MutableMapping[str, CoordinateModel]
parameter_names: List[str]
@@ -21,28 +22,32 @@ class CurveModel(object):
to_weierstrass: List[Module]
from_weierstrass: List[Module]
- def __init_subclass__(cls, efd_name: str = "", **kwargs):
- cls._efd_name = efd_name
+ def __init__(self, efd_name: str):
+ self._efd_name = efd_name
+ if self._loaded:
+ return
+ else:
+ self.__class__._loaded = True
+ self.__class__.coordinates = {}
+ self.__class__.parameter_names = []
+ self.__class__.coordinate_names = []
+ self.__class__.base_addition = []
+ self.__class__.base_doubling = []
+ self.__class__.base_negation = []
+ self.__class__.base_neutral = []
+ self.__class__.full_weierstrass = []
+ self.__class__.to_weierstrass = []
+ self.__class__.from_weierstrass = []
+
files = resource_listdir(__name__, "efd/" + efd_name)
- cls.coordinates = {}
- cls.parameter_names = []
- cls.coordinate_names = []
- cls.base_addition = []
- cls.base_doubling = []
- cls.base_negation = []
- cls.base_neutral = []
- cls.full_weierstrass = []
- cls.to_weierstrass = []
- cls.from_weierstrass = []
for fname in files:
file_path = "efd/" + efd_name + "/" + fname
if resource_isdir(__name__, file_path):
- cls.__read_coordinate_dir(file_path, fname)
+ self.__read_coordinate_dir(file_path, fname)
else:
- cls.__read_curve_file(file_path)
+ self.__read_curve_file(self.__class__, file_path)
- @classmethod
- def __read_curve_file(cls, file_path):
+ def __read_curve_file(self, cls, file_path):
def format_eq(line, mode="exec"):
return parse(line.replace("^", "**"), mode=mode)
@@ -69,31 +74,38 @@ class CurveModel(object):
elif line.startswith("toweierstrass"):
cls.to_weierstrass.append(format_eq(line[14:]))
elif line.startswith("fromweierstrass"):
- cls.to_weierstrass.append(format_eq(line[16:]))
+ cls.from_weierstrass.append(format_eq(line[16:]))
else:
cls.full_weierstrass.append(format_eq(line))
line = f.readline()
- @classmethod
- def __read_coordinate_dir(cls, dir_path, name):
- cls.coordinates[name] = CoordinateModel(dir_path, name, cls)
+ def __read_coordinate_dir(self, dir_path, name):
+ self.coordinates[name] = CoordinateModel(dir_path, name, self)
@public
-class ShortWeierstrassModel(CurveModel, efd_name="shortw"):
- pass
+class ShortWeierstrassModel(CurveModel):
+
+ def __init__(self):
+ super().__init__("shortw")
@public
-class MontgomeryModel(CurveModel, efd_name="montgom"):
- pass
+class MontgomeryModel(CurveModel):
+
+ def __init__(self):
+ super().__init__("montgom")
@public
-class EdwardsModel(CurveModel, efd_name="edwards"):
- pass
+class EdwardsModel(CurveModel):
+
+ def __init__(self):
+ super().__init__("edwards")
@public
-class TwistedEdwardsModel(CurveModel, efd_name="twisted"):
- pass
+class TwistedEdwardsModel(CurveModel):
+
+ def __init__(self):
+ super().__init__("twisted")
diff --git a/pyecsca/ec/mult.py b/pyecsca/ec/mult.py
index 9255342..9870296 100644
--- a/pyecsca/ec/mult.py
+++ b/pyecsca/ec/mult.py
@@ -1,6 +1,7 @@
from copy import copy
-from typing import Mapping, Tuple, Optional
+from typing import Mapping, Tuple, Optional, List
+from pyecsca.ec.naf import naf, wnaf
from .context import Context
from .curve import EllipticCurve
from .formula import Formula, AdditionFormula, DoublingFormula, ScalingFormula, LadderFormula
@@ -11,6 +12,7 @@ class ScalarMultiplier(object):
curve: EllipticCurve
formulas: Mapping[str, Formula]
context: Context
+ _point: Optional[Point] = None
def __init__(self, curve: EllipticCurve, ctx: Context = None, **formulas: Optional[Formula]):
for formula in formulas.values():
@@ -44,13 +46,20 @@ class ScalarMultiplier(object):
raise NotImplementedError
return self.context.execute(self.formulas["scl"], point, **self.curve.parameters)[0]
- def _ladd(self, start: Point, to_dbl: Point, to_add: Point) -> Tuple[Point, Point]:
+ def _ladd(self, start: Point, to_dbl: Point, to_add: Point) -> Tuple[Point, ...]:
if "ladd" not in self.formulas:
raise NotImplementedError
return self.context.execute(self.formulas["ladd"], start, to_dbl, to_add,
**self.curve.parameters)
- def multiply(self, scalar: int, point: Point) -> Point:
+ def _neg(self, point: Point) -> Point:
+ #TODO
+ raise NotImplementedError
+
+ def init(self, point: Point):
+ raise NotImplementedError
+
+ def multiply(self, scalar: int, point: Optional[Point]) -> Point:
raise NotImplementedError
@@ -63,14 +72,19 @@ class LTRMultiplier(ScalarMultiplier):
super().__init__(curve, ctx, add=add, dbl=dbl, scl=scl)
self.always = always
- def multiply(self, scalar: int, point: Point) -> Point:
+ def init(self, point: Point):
+ self._point = point
+
+ def multiply(self, scalar: int, point: Optional[Point]) -> Point:
+ if point is not None and self._point != point:
+ self.init(point)
r = copy(self.curve.neutral)
for i in range(scalar.bit_length(), -1, -1):
r = self._dbl(r)
if scalar & (1 << i) != 0:
- r = self._add(r, point)
+ r = self._add(r, self._point)
elif self.always:
- self._add(r, point)
+ self._add(r, self._point)
if "scl" in self.formulas:
r = self._scl(r)
return r
@@ -85,8 +99,14 @@ class RTLMultiplier(ScalarMultiplier):
super().__init__(curve, ctx, add=add, dbl=dbl, scl=scl)
self.always = always
- def multiply(self, scalar: int, point: Point) -> Point:
+ def init(self, point: Point):
+ self._point = point
+
+ def multiply(self, scalar: int, point: Optional[Point]) -> Point:
+ if point is not None and self._point != point:
+ self.init(point)
r = copy(self.curve.neutral)
+ point = self._point
while scalar > 0:
if scalar & 1 != 0:
r = self._add(r, point)
@@ -105,14 +125,74 @@ class LadderMultiplier(ScalarMultiplier):
ctx: Context = None):
super().__init__(curve, ctx, ladd=ladd, scl=scl)
- def multiply(self, scalar: int, point: Point) -> Point:
- p0 = copy(point)
- p1 = self._ladd(self.curve.neutral, point, point)[1]
+ def init(self, point: Point):
+ self._point = point
+
+ def multiply(self, scalar: int, point: Optional[Point]) -> Point:
+ if point is not None and self._point != point:
+ self.init(point)
+ p0 = copy(self._point)
+ p1 = self._ladd(self.curve.neutral, self._point, self._point)[1]
for i in range(scalar.bit_length(), -1, -1):
if scalar & i != 0:
- p0, p1 = self._ladd(point, p1, p0)
+ p0, p1 = self._ladd(self._point, p1, p0)
else:
- p0, p1 = self._ladd(point, p0, p1)
+ p0, p1 = self._ladd(self._point, p0, p1)
if "scl" in self.formulas:
p0 = self._scl(p0)
return p0
+
+
+class BinaryNAFMultiplier(ScalarMultiplier):
+ _point_neg: Point
+
+ def __init__(self, curve: EllipticCurve, add: AdditionFormula, dbl: DoublingFormula,
+ scl: ScalingFormula = None,
+ ctx: Context = None):
+ super().__init__(curve, ctx, add=add, dbl=dbl, scl=scl)
+
+ def init(self, point: Point):
+ self._point = point
+ self._point_neg = self._neg(point)
+
+ def multiply(self, scalar: int, point: Optional[Point]) -> Point:
+ if point is not None and self._point != point:
+ self.init(point)
+ bnaf = naf(scalar)
+ q = copy(self.curve.neutral)
+ for val in bnaf:
+ q = self._dbl(q)
+ if val == 1:
+ q = self._add(q, self._point)
+ if val == -1:
+ q = self._add(q, self._point_neg)
+ return q
+
+
+class WindowNAFMultiplier(ScalarMultiplier):
+ _points: List[Point]
+ _width: int
+
+ def __init__(self, curve: EllipticCurve, add: AdditionFormula, dbl: DoublingFormula, width: int,
+ scl: ScalingFormula = None,
+ ctx: Context = None):
+ super().__init__(curve, ctx, add=add, dbl=dbl, scl=scl)
+ self._width = width
+
+ def init(self, point: Point):
+ self._point = point
+ # TODO: precompute {1, 3, 5, upto 2^(w-1)-1}
+
+ def multiply(self, scalar: int, point: Optional[Point]):
+ if point is not None and self._point != point:
+ self.init(point)
+ naf = wnaf(scalar, self._width)
+ q = copy(self.curve.neutral)
+ for val in naf:
+ q = self._dbl(q)
+ if val > 0:
+ q = self._add(q, self._points[val])
+ elif val < 0:
+ neg = self._neg(self._points[-val])
+ q = self._add(q, neg)
+ return q
diff --git a/pyecsca/ec/naf.py b/pyecsca/ec/naf.py
new file mode 100644
index 0000000..855ffd1
--- /dev/null
+++ b/pyecsca/ec/naf.py
@@ -0,0 +1,30 @@
+from public import public
+from typing import List
+
+
+@public
+def wnaf(k: int, w: int) -> List[int]:
+ half_width = 2 ** (w - 1)
+ full_width = half_width * 2
+
+ def mods(val: int):
+ val_mod = val % full_width
+ if val_mod > half_width:
+ val_mod -= full_width
+ return val_mod
+
+ result = []
+ while k >= 1:
+ if k & 1:
+ k_val = mods(k)
+ result.insert(0, k_val)
+ k -= k_val
+ else:
+ result.insert(0, 0)
+ k >>= 1
+ return result
+
+
+@public
+def naf(k: int) -> List[int]:
+ return wnaf(k, 2)
diff --git a/pyecsca/sca/trace_set/inspector.py b/pyecsca/sca/trace_set/inspector.py
index bcffb94..634b960 100644
--- a/pyecsca/sca/trace_set/inspector.py
+++ b/pyecsca/sca/trace_set/inspector.py
@@ -100,7 +100,7 @@ class InspectorTraceSet(TraceSet):
external_clock_frequencty: float = 0
external_clock_time_base: int = 0
- _raw_traces: List[Trace] = None
+ _raw_traces: Optional[List[Trace]] = None
_tag_parsers: dict = {
0x41: ("num_traces", 4, Parsers.read_int, Parsers.write_int),
0x42: ("num_samples", 4, Parsers.read_int, Parsers.write_int),
diff --git a/pyecsca/sca/ttest.py b/pyecsca/sca/ttest.py
index b9ecd36..34e631c 100644
--- a/pyecsca/sca/ttest.py
+++ b/pyecsca/sca/ttest.py
@@ -6,7 +6,7 @@ from typing import Sequence, Optional
from .trace import Trace, CombinedTrace
-def ttest(first_set: Sequence[Trace], second_set: Sequence[Trace],
+def ttest_func(first_set: Sequence[Trace], second_set: Sequence[Trace],
equal_var: bool) -> Optional[CombinedTrace]:
if not first_set or not second_set or len(first_set) == 0 or len(second_set) == 0:
return None
@@ -25,7 +25,7 @@ def welch_ttest(first_set: Sequence[Trace], second_set: Sequence[Trace]) -> Comb
:param second_set:
:return: Welch's t-values (samplewise)
"""
- return ttest(first_set, second_set, False)
+ return ttest_func(first_set, second_set, False)
@public
def student_ttest(first_set: Sequence[Trace], second_set: Sequence[Trace]) -> CombinedTrace:
@@ -37,4 +37,4 @@ def student_ttest(first_set: Sequence[Trace], second_set: Sequence[Trace]) -> Co
:param second_set:
:return: Student's t-values (samplewise)
"""
- return ttest(first_set, second_set, True)
+ return ttest_func(first_set, second_set, True)
diff --git a/test/ec/test_model.py b/test/ec/test_model.py
index 6effa45..b9d4383 100644
--- a/test/ec/test_model.py
+++ b/test/ec/test_model.py
@@ -7,7 +7,7 @@ from pyecsca.ec.model import (ShortWeierstrassModel, MontgomeryModel, EdwardsMod
class CurveModelTests(TestCase):
def test_load(self):
- self.assertGreater(len(ShortWeierstrassModel.coordinates), 0)
- self.assertGreater(len(MontgomeryModel.coordinates), 0)
- self.assertGreater(len(EdwardsModel.coordinates), 0)
- self.assertGreater(len(TwistedEdwardsModel.coordinates), 0)
+ self.assertGreater(len(ShortWeierstrassModel().coordinates), 0)
+ self.assertGreater(len(MontgomeryModel().coordinates), 0)
+ self.assertGreater(len(EdwardsModel().coordinates), 0)
+ self.assertGreater(len(TwistedEdwardsModel().coordinates), 0)
diff --git a/test/ec/test_mult.py b/test/ec/test_mult.py
index 38c7367..1a2bc7e 100644
--- a/test/ec/test_mult.py
+++ b/test/ec/test_mult.py
@@ -11,21 +11,21 @@ class ScalarMultiplierTests(TestCase):
def setUp(self):
self.p = 0xfffffffdffffffffffffffffffffffff
- self.coords = ShortWeierstrassModel.coordinates["projective"]
+ self.coords = ShortWeierstrassModel().coordinates["projective"]
self.base = Point(self.coords, X=Mod(0x161ff7528b899b2d0c28607ca52c5b86, self.p),
Y=Mod(0xcf5ac8395bafeb13c02da292dded7a83, self.p),
Z=Mod(1, self.p))
- self.secp128r1 = EllipticCurve(ShortWeierstrassModel, self.coords,
+ self.secp128r1 = EllipticCurve(ShortWeierstrassModel(), self.coords,
dict(a=0xfffffffdfffffffffffffffffffffffc,
b=0xe87579c11079f43dd824993c2cee5ed3),
Point(self.coords, X=Mod(0, self.p), Y=Mod(1, self.p),
Z=Mod(0, self.p)))
- self.coords25519 = MontgomeryModel.coordinates["xz"]
+ self.coords25519 = MontgomeryModel().coordinates["xz"]
self.p25519 = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed
self.base25519 = Point(self.coords25519, X=Mod(9, self.p25519),
Z=Mod(1, self.p25519))
- self.curve25519 = EllipticCurve(MontgomeryModel, self.coords25519,
+ self.curve25519 = EllipticCurve(MontgomeryModel(), self.coords25519,
dict(a=486662, b=1),
Point(self.coords25519,
X=Mod(0, self.p25519), Z=Mod(1, self.p25519)))
@@ -62,3 +62,14 @@ class ScalarMultiplierTests(TestCase):
self.coords.formulas["dbl-1998-cmo"], self.coords.formulas["z"])
res_rtl = rtl.multiply(10, self.base)
self.assertEqual(res_ltr, res_rtl)
+
+ ltr_always = LTRMultiplier(self.secp128r1, self.coords.formulas["add-1998-cmo"],
+ self.coords.formulas["dbl-1998-cmo"], self.coords.formulas["z"],
+ always=True)
+ rtl_always = RTLMultiplier(self.secp128r1, self.coords.formulas["add-1998-cmo"],
+ self.coords.formulas["dbl-1998-cmo"], self.coords.formulas["z"],
+ always=True)
+ res_ltr_always = ltr_always.multiply(10, self.base)
+ res_rtl_always = rtl_always.multiply(10, self.base)
+ self.assertEqual(res_ltr, res_ltr_always)
+ self.assertEqual(res_rtl, res_rtl_always)
diff --git a/test/ec/test_naf.py b/test/ec/test_naf.py
new file mode 100644
index 0000000..c87c03f
--- /dev/null
+++ b/test/ec/test_naf.py
@@ -0,0 +1,10 @@
+from unittest import TestCase
+
+from pyecsca.ec.naf import naf, wnaf
+
+
+class NafTests(TestCase):
+
+ def test_nafs(self):
+ i = 0b1100110101001101011011
+ self.assertListEqual(naf(i), wnaf(i, 2))