aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJ08nY2020-02-11 20:44:45 +0100
committerJ08nY2020-02-11 20:44:45 +0100
commit11bd56b296f1620932f098a6037f0807e7f6616f (patch)
tree2a791114a710ab49af523cf1ba2144646ef9ad90
parent4e2bd346baf2db39391deb49e9bdb9d89f94101a (diff)
downloadpyecsca-11bd56b296f1620932f098a6037f0807e7f6616f.tar.gz
pyecsca-11bd56b296f1620932f098a6037f0807e7f6616f.tar.bz2
pyecsca-11bd56b296f1620932f098a6037f0807e7f6616f.zip
Rework context handling into a tree-based approach.
-rw-r--r--Makefile4
-rw-r--r--pyecsca/ec/context.py173
-rw-r--r--pyecsca/ec/formula.py123
-rw-r--r--pyecsca/ec/key_agreement.py86
-rw-r--r--pyecsca/ec/key_generation.py44
-rw-r--r--pyecsca/ec/mod.py20
-rw-r--r--pyecsca/ec/mult.py301
-rw-r--r--pyecsca/ec/op.py11
-rw-r--r--pyecsca/ec/point.py88
-rw-r--r--pyecsca/ec/signature.py169
-rw-r--r--test/ec/test_context.py30
-rw-r--r--test/ec/test_formula.py35
-rw-r--r--test/ec/test_key_agreement.py12
-rw-r--r--test/ec/test_key_generation.py26
-rw-r--r--test/ec/test_op.py9
-rw-r--r--test/ec/test_signature.py13
16 files changed, 714 insertions, 430 deletions
diff --git a/Makefile b/Makefile
index ad8fd0f..369a065 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
-EC_TESTS = ec.test_context ec.test_curve ec.test_curves ec.test_params ec.test_key_agreement ec.test_mod ec.test_model \
-ec.test_mult ec.test_naf ec.test_op ec.test_point ec.test_signature
+EC_TESTS = ec.test_context ec.test_curve ec.test_curves ec.test_formula ec.test_params ec.test_key_agreement ec.test_key_generation \
+ec.test_mod ec.test_model ec.test_mult ec.test_naf ec.test_op ec.test_point ec.test_signature
SCA_TESTS = sca.test_align sca.test_combine sca.test_edit sca.test_filter sca.test_match sca.test_process \
sca.test_sampling sca.test_test sca.test_trace sca.test_traceset
diff --git a/pyecsca/ec/context.py b/pyecsca/ec/context.py
index 8f797a8..d6f56af 100644
--- a/pyecsca/ec/context.py
+++ b/pyecsca/ec/context.py
@@ -1,85 +1,45 @@
-import ast
from abc import ABCMeta, abstractmethod
+from collections import OrderedDict
from contextvars import ContextVar, Token
from copy import deepcopy
-from typing import List, Tuple, Optional, Union, MutableMapping, Any, ContextManager
+from typing import List, Optional, ContextManager, Any
from public import public
-from .formula import Formula
-from .mod import Mod
-from .op import CodeOp, OpType
-from .point import Point
-
@public
-class OpResult(object):
- """A result of an operation."""
- parents: Tuple
- op: OpType
- name: str
- value: Mod
-
- def __init__(self, name: str, value: Mod, op: OpType, *parents: Any):
- self.parents = tuple(parents)
- self.name = name
- self.value = value
- self.op = op
+class Action(object):
+ """An Action."""
+ inside: bool
- def __str__(self):
- return self.name
+ def __init__(self):
+ self.inside = False
- def __repr__(self):
- char = self.op.op_str
- parents = char.join(str(parent) for parent in self.parents)
- return f"{self.name} = {parents}"
+ def __enter__(self):
+ getcontext().enter_action(self)
+ self.inside = True
+ return self
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ getcontext().exit_action(self)
+ self.inside = False
-@public
-class Action(object):
- """An execution of some operations with inputs and outputs."""
- inputs: MutableMapping[str, Mod]
- input_points: List[Point]
- intermediates: MutableMapping[str, OpResult]
- outputs: MutableMapping[str, OpResult]
- output_points: List[Point]
-
- def __init__(self, *points: Point, **inputs: Mod):
- self.inputs = inputs
- self.intermediates = {}
- self.outputs = {}
- self.input_points = list(points)
- self.output_points = []
-
- def add_operation(self, op: CodeOp, value: Mod):
- parents: List[Union[Mod, OpResult]] = []
- for parent in {*op.variables, *op.parameters}:
- if parent in self.intermediates:
- parents.append(self.intermediates[parent])
- elif parent in self.inputs:
- parents.append(self.inputs[parent])
- self.intermediates[op.result] = OpResult(op.result, value, op.operator, *parents)
-
- def add_result(self, point: Point, **outputs: Mod):
- for k in outputs:
- self.outputs[k] = self.intermediates[k]
- self.output_points.append(point)
- def __repr__(self):
- return f"{self.__class__.__name__}({self.input_points}) = {self.output_points}"
-@public
-class FormulaAction(Action):
- """An execution of a formula, on some input points and parameters, with some outputs."""
- formula: Formula
- def __init__(self, formula: Formula, *points: Point, **inputs: Mod):
- super().__init__(*points, **inputs)
- self.formula = formula
+class Tree(OrderedDict):
- def __repr__(self):
- return f"{self.__class__.__name__}({self.formula}, {self.input_points}) = {self.output_points}"
+ def walk_get(self, path: List) -> Any:
+ if len(path) == 0:
+ return self
+ value = self[path[0]]
+ if isinstance(value, Tree):
+ return value.walk_get(path[1:])
+ elif len(path) == 1:
+ return value
+ else:
+ raise ValueError
@public
@@ -87,92 +47,49 @@ class Context(object):
__metaclass__ = ABCMeta
@abstractmethod
- def _log_formula(self, formula: Formula, *points: Point, **inputs: Mod):
+ def enter_action(self, action: Action):
...
@abstractmethod
- def _log_operation(self, op: CodeOp, value: Mod):
+ def exit_action(self, action: Action):
...
- @abstractmethod
- def _log_result(self, point: Point, **outputs: Mod):
- ...
-
- def _execute(self, formula: Formula, *points: Point, **params: Mod) -> Tuple[Point, ...]:
- if len(points) != formula.num_inputs:
- raise ValueError(f"Wrong number of inputs for {formula}.")
- coords = {}
- for i, point in enumerate(points):
- if point.coordinate_model != formula.coordinate_model:
- raise ValueError(f"Wrong coordinate model of point {point}.")
- for coord, value in point.coords.items():
- coords[coord + str(i + 1)] = value
- locals = {**coords, **params}
- self._log_formula(formula, *points, **locals)
- for op in formula.code:
- op_result = op(**locals)
- self._log_operation(op, op_result)
- locals[op.result] = op_result
- result = []
- for i in range(formula.num_outputs):
- ind = str(i + formula.output_index)
- resulting = {}
- full_resulting = {}
- for variable in formula.coordinate_model.variables:
- full_variable = variable + ind
- resulting[variable] = locals[full_variable]
- full_resulting[full_variable] = locals[full_variable]
- point = Point(formula.coordinate_model, **resulting)
-
- self._log_result(point, **full_resulting)
- result.append(point)
- return tuple(result)
-
- def execute(self, formula: Formula, *points: Point, **params: Mod) -> Tuple[Point, ...]:
- """
- Execute a formula.
-
- :param formula: Formula to execute.
- :param points: Points to pass into the formula.
- :param params: Parameters of the curve.
- :return: The resulting point(s).
- """
- return self._execute(formula, *points, **params)
-
def __str__(self):
return self.__class__.__name__
@public
class NullContext(Context):
- """A context that does not trace any operations."""
+ """A context that does not trace any actions."""
- def _log_formula(self, formula: Formula, *points: Point, **inputs: Mod):
+ def enter_action(self, action: Action):
pass
- def _log_operation(self, op: CodeOp, value: Mod):
- pass
-
- def _log_result(self, point: Point, **outputs: Mod):
+ def exit_action(self, action: Action):
pass
@public
class DefaultContext(Context):
- """A context that traces executions of formulas."""
- actions: List[FormulaAction]
+ """A context that traces executions of actions."""
+ actions: Tree
+ current: List[Action]
- def __init__(self):
- self.actions = []
+ def enter_action(self, action: Action):
+ self.actions.walk_get(self.current)[action] = Tree()
+ self.current.append(action)
- def _log_formula(self, formula: Formula, *points: Point, **inputs: Mod):
- self.actions.append(FormulaAction(formula, *points, **inputs))
+ def exit_action(self, action: Action):
+ if self.current[-1] != action:
+ raise ValueError
+ self.current.pop()
- def _log_operation(self, op: CodeOp, value: Mod):
- self.actions[-1].add_operation(op, value)
+ def __init__(self):
+ self.actions = Tree()
+ self.current = []
- def _log_result(self, point: Point, **outputs: Mod):
- self.actions[-1].add_result(point, **outputs)
+ def __repr__(self):
+ return f"{self.__class__.__name__}({self.actions}, current={self.current})"
_actual_context: ContextVar[Context] = ContextVar("operational_context", default=NullContext())
diff --git a/pyecsca/ec/formula.py b/pyecsca/ec/formula.py
index aeed4a6..a3875ec 100644
--- a/pyecsca/ec/formula.py
+++ b/pyecsca/ec/formula.py
@@ -1,11 +1,74 @@
from ast import parse, Expression, Mult, Add, Sub, Pow, Div
from itertools import product
-from typing import List, Set, Any, ClassVar, MutableMapping
+from typing import List, Set, Any, ClassVar, MutableMapping, Tuple, Union
from pkg_resources import resource_stream
from public import public
-from .op import CodeOp
+from .context import Action
+from .mod import Mod
+from .op import CodeOp, OpType
+
+
+@public
+class OpResult(object):
+ """A result of an operation."""
+ parents: Tuple
+ op: OpType
+ name: str
+ value: Mod
+
+ def __init__(self, name: str, value: Mod, op: OpType, *parents: Any):
+ self.parents = tuple(parents)
+ self.name = name
+ self.value = value
+ self.op = op
+
+ def __str__(self):
+ return self.name
+
+ def __repr__(self):
+ char = self.op.op_str
+ parents = char.join(str(parent) for parent in self.parents)
+ return f"{self.name} = {parents}"
+
+
+@public
+class FormulaAction(Action):
+ """An execution of a formula, on some input points and parameters, with some outputs."""
+ formula: "Formula"
+ inputs: MutableMapping[str, Mod]
+ input_points: List[Any]
+ intermediates: MutableMapping[str, OpResult]
+ outputs: MutableMapping[str, OpResult]
+ output_points: List[Any]
+
+ def __init__(self, formula: "Formula", *points: Any,
+ **inputs: Mod):
+ super().__init__()
+ self.formula = formula
+ self.inputs = inputs
+ self.intermediates = {}
+ self.outputs = {}
+ self.input_points = list(points)
+ self.output_points = []
+
+ def add_operation(self, op: CodeOp, value: Mod):
+ parents: List[Union[Mod, OpResult]] = []
+ for parent in {*op.variables, *op.parameters}:
+ if parent in self.intermediates:
+ parents.append(self.intermediates[parent])
+ elif parent in self.inputs:
+ parents.append(self.inputs[parent])
+ self.intermediates[op.result] = OpResult(op.result, value, op.operator, *parents)
+
+ def add_result(self, point: Any, **outputs: Mod):
+ for k in outputs:
+ self.outputs[k] = self.intermediates[k]
+ self.output_points.append(point)
+
+ def __repr__(self):
+ return f"{self.__class__.__name__}({self.formula}, {self.input_points}) = {self.output_points}"
class Formula(object):
@@ -20,6 +83,44 @@ class Formula(object):
num_inputs: ClassVar[int]
num_outputs: ClassVar[int]
+ def __call__(self, *points: Any, **params: Mod) -> Tuple[Any, ...]:
+ """
+ Execute a formula.
+
+ :param points: Points to pass into the formula.
+ :param params: Parameters of the curve.
+ :return: The resulting point(s).
+ """
+ from .point import Point
+ if len(points) != self.num_inputs:
+ raise ValueError(f"Wrong number of inputs for {self}.")
+ coords = {}
+ for i, point in enumerate(points):
+ if point.coordinate_model != self.coordinate_model:
+ raise ValueError(f"Wrong coordinate model of point {point}.")
+ for coord, value in point.coords.items():
+ coords[coord + str(i + 1)] = value
+ locals = {**coords, **params}
+ with FormulaAction(self, *points, **locals) as action:
+ for op in self.code:
+ op_result = op(**locals)
+ action.add_operation(op, op_result)
+ locals[op.result] = op_result
+ result = []
+ for i in range(self.num_outputs):
+ ind = str(i + self.output_index)
+ resulting = {}
+ full_resulting = {}
+ for variable in self.coordinate_model.variables:
+ full_variable = variable + ind
+ resulting[variable] = locals[full_variable]
+ full_resulting[full_variable] = locals[full_variable]
+ point = Point(self.coordinate_model, **resulting)
+
+ action.add_result(point, **full_resulting)
+ result.append(point)
+ return tuple(result)
+
def __repr__(self):
return f"{self.__class__.__name__}({self.name} for {self.coordinate_model})"
@@ -51,22 +152,32 @@ class Formula(object):
@property
def num_multiplications(self) -> int:
"""Number of multiplications."""
- return len(list(filter(lambda op: isinstance(op.operator, Mult), self.code)))
+ return len(list(filter(lambda op: op.operator == OpType.Mult, self.code)))
+
+ @property
+ def num_divisions(self) -> int:
+ """Number of divisions."""
+ return len(list(filter(lambda op: op.operator == OpType.Div, self.code)))
@property
def num_inversions(self) -> int:
"""Number of inversions."""
- return len(list(filter(lambda op: isinstance(op.operator, Div), self.code)))
+ return len(list(filter(lambda op: op.operator == OpType.Inv, self.code)))
+
+ @property
+ def num_powers(self) -> int:
+ """Number of powers."""
+ return len(list(filter(lambda op: op.operator == OpType.Pow, self.code)))
@property
def num_squarings(self) -> int:
"""Number of squarings."""
- return len(list(filter(lambda op: isinstance(op.operator, Pow), self.code)))
+ return len(list(filter(lambda op: op.operator == OpType.Sqr, self.code)))
@property
def num_addsubs(self) -> int:
"""Number of additions and subtractions."""
- return len(list(filter(lambda op: isinstance(op.operator, (Add, Sub)), self.code)))
+ return len(list(filter(lambda op: op.operator == OpType.Add or op.operator == OpType.Sub, self.code)))
class EFDFormula(Formula):
diff --git a/pyecsca/ec/key_agreement.py b/pyecsca/ec/key_agreement.py
index 3071ffb..6d6728f 100644
--- a/pyecsca/ec/key_agreement.py
+++ b/pyecsca/ec/key_agreement.py
@@ -3,28 +3,51 @@ from typing import Optional, Any
from public import public
-from .params import DomainParameters
+from .context import Action
+from .mod import Mod
from .mult import ScalarMultiplier
+from .params import DomainParameters
from .point import Point
@public
+class ECDHAction(Action):
+ """An ECDH key exchange."""
+ params: DomainParameters
+ hash_algo: Optional[Any]
+ privkey: Mod
+ pubkey: Point
+
+ def __init__(self, params: DomainParameters, hash_algo: Optional[Any],
+ privkey: Mod,
+ pubkey: Point):
+ super().__init__()
+ self.params = params
+ self.hash_algo = hash_algo
+ self.privkey = privkey
+ self.pubkey = pubkey
+
+ def __repr__(self):
+ return f"{self.__class__.__name__}({self.params}, {self.hash_algo}, {self.privkey}, {self.pubkey})"
+
+
+@public
class KeyAgreement(object):
"""An EC based key agreement primitive. (ECDH)"""
mult: ScalarMultiplier
- group: DomainParameters
+ params: DomainParameters
pubkey: Point
- privkey: int
+ privkey: Mod
hash_algo: Optional[Any]
- def __init__(self, mult: ScalarMultiplier, group: DomainParameters, pubkey: Point, privkey: int,
+ def __init__(self, mult: ScalarMultiplier, params: DomainParameters, pubkey: Point, privkey: Mod,
hash_algo: Optional[Any] = None):
self.mult = mult
- self.group = group
+ self.params = params
self.pubkey = pubkey
self.privkey = privkey
self.hash_algo = hash_algo
- self.mult.init(self.group, self.pubkey)
+ self.mult.init(self.params, self.pubkey)
def perform_raw(self) -> Point:
"""
@@ -32,8 +55,8 @@ class KeyAgreement(object):
:return: The shared point.
"""
- point = self.mult.multiply(self.privkey)
- return point.to_affine() # TODO: This conversion should be somehow added to the context
+ point = self.mult.multiply(int(self.privkey))
+ return point.to_affine()
def perform(self) -> bytes:
"""
@@ -41,59 +64,66 @@ class KeyAgreement(object):
:return: The shared secret.
"""
- affine_point = self.perform_raw()
- x = int(affine_point.x)
- p = self.group.curve.prime
- n = (p.bit_length() + 7) // 8
- result = x.to_bytes(n, byteorder="big")
- if self.hash_algo is not None:
- result = self.hash_algo(result).digest()
- return result
+ with ECDHAction(self.params, self.hash_algo, self.privkey, self.pubkey):
+ affine_point = self.perform_raw()
+ x = int(affine_point.x)
+ p = self.params.curve.prime
+ n = (p.bit_length() + 7) // 8
+ result = x.to_bytes(n, byteorder="big")
+ if self.hash_algo is not None:
+ result = self.hash_algo(result).digest()
+ return result
@public
class ECDH_NONE(KeyAgreement):
"""Raw x-coordinate ECDH."""
- def __init__(self, mult: ScalarMultiplier, group: DomainParameters, pubkey: Point, privkey: int):
- super().__init__(mult, group, pubkey, privkey)
+ def __init__(self, mult: ScalarMultiplier, params: DomainParameters, pubkey: Point,
+ privkey: Mod):
+ super().__init__(mult, params, pubkey, privkey)
@public
class ECDH_SHA1(KeyAgreement):
"""ECDH with SHA1 of x-coordinate."""
- def __init__(self, mult: ScalarMultiplier, group: DomainParameters, pubkey: Point, privkey: int):
- super().__init__(mult, group, pubkey, privkey, hashlib.sha1)
+ def __init__(self, mult: ScalarMultiplier, params: DomainParameters, pubkey: Point,
+ privkey: Mod):
+ super().__init__(mult, params, pubkey, privkey, hashlib.sha1)
@public
class ECDH_SHA224(KeyAgreement):
"""ECDH with SHA224 of x-coordinate."""
- def __init__(self, mult: ScalarMultiplier, group: DomainParameters, pubkey: Point, privkey: int):
- super().__init__(mult, group, pubkey, privkey, hashlib.sha224)
+ def __init__(self, mult: ScalarMultiplier, params: DomainParameters, pubkey: Point,
+ privkey: Mod):
+ super().__init__(mult, params, pubkey, privkey, hashlib.sha224)
@public
class ECDH_SHA256(KeyAgreement):
"""ECDH with SHA256 of x-coordinate."""
- def __init__(self, mult: ScalarMultiplier, group: DomainParameters, pubkey: Point, privkey: int):
- super().__init__(mult, group, pubkey, privkey, hashlib.sha256)
+ def __init__(self, mult: ScalarMultiplier, params: DomainParameters, pubkey: Point,
+ privkey: Mod):
+ super().__init__(mult, params, pubkey, privkey, hashlib.sha256)
@public
class ECDH_SHA384(KeyAgreement):
"""ECDH with SHA384 of x-coordinate."""
- def __init__(self, mult: ScalarMultiplier, group: DomainParameters, pubkey: Point, privkey: int):
- super().__init__(mult, group, pubkey, privkey, hashlib.sha384)
+ def __init__(self, mult: ScalarMultiplier, params: DomainParameters, pubkey: Point,
+ privkey: Mod):
+ super().__init__(mult, params, pubkey, privkey, hashlib.sha384)
@public
class ECDH_SHA512(KeyAgreement):
"""ECDH with SHA512 of x-coordinate."""
- def __init__(self, mult: ScalarMultiplier, group: DomainParameters, pubkey: Point, privkey: int):
- super().__init__(mult, group, pubkey, privkey, hashlib.sha512)
+ def __init__(self, mult: ScalarMultiplier, params: DomainParameters, pubkey: Point,
+ privkey: Mod):
+ super().__init__(mult, params, pubkey, privkey, hashlib.sha512)
diff --git a/pyecsca/ec/key_generation.py b/pyecsca/ec/key_generation.py
new file mode 100644
index 0000000..0813dbd
--- /dev/null
+++ b/pyecsca/ec/key_generation.py
@@ -0,0 +1,44 @@
+from typing import Tuple
+
+from public import public
+
+from .context import Action
+from .mod import Mod
+from .mult import ScalarMultiplier
+from .params import DomainParameters
+from .point import Point
+
+
+@public
+class KeygenAction(Action):
+ """A key generation."""
+ params: DomainParameters
+
+ def __init__(self, params: DomainParameters):
+ super().__init__()
+ self.params = params
+
+ def __repr__(self):
+ return f"{self.__class__.__name__}({self.params})"
+
+
+@public
+class KeyGeneration(object):
+ """Key generator."""
+ mult: ScalarMultiplier
+ params: DomainParameters
+ affine: bool
+
+ def __init__(self, mult: ScalarMultiplier, params: DomainParameters, affine: bool = False):
+ self.mult = mult
+ self.params = params
+ self.mult.init(self.params, self.params.generator)
+ self.affine = affine
+
+ def generate(self) -> Tuple[Mod, Point]:
+ with KeygenAction(self.params):
+ privkey = Mod.random(self.params.order)
+ pubkey = self.mult.multiply(privkey.x)
+ if self.affine:
+ pubkey = pubkey.to_affine()
+ return privkey, pubkey
diff --git a/pyecsca/ec/mod.py b/pyecsca/ec/mod.py
index a142b71..2b16a07 100644
--- a/pyecsca/ec/mod.py
+++ b/pyecsca/ec/mod.py
@@ -1,7 +1,10 @@
+import secrets
from functools import wraps
from public import public
+from .context import Action
+
@public
def gcd(a, b):
@@ -48,6 +51,18 @@ def check(func):
return method
+@public
+class RandomModAction(Action):
+ """A random sampling from Z_n."""
+ order: int
+
+ def __init__(self, order: int):
+ super().__init__()
+ self.order = order
+
+ def __repr__(self):
+ return f"{self.__class__.__name__}({self.order:x})"
+
@public
class Mod(object):
@@ -120,6 +135,11 @@ class Mod(object):
q, r = divmod(self.x, divisor.x)
return Mod(q, self.n), Mod(r, self.n)
+ @staticmethod
+ def random(n: int):
+ with RandomModAction(n):
+ return Mod(secrets.randbelow(n), n)
+
def __int__(self):
return self.x
diff --git a/pyecsca/ec/mult.py b/pyecsca/ec/mult.py
index 4f937fa..4c5b6d9 100644
--- a/pyecsca/ec/mult.py
+++ b/pyecsca/ec/mult.py
@@ -1,16 +1,31 @@
from copy import copy
-from typing import Mapping, Tuple, Optional, MutableMapping, Union, ClassVar, Set, Type
+from typing import Mapping, Tuple, Optional, MutableMapping, ClassVar, Set, Type
from public import public
-from .context import getcontext
+from .context import Action
from .formula import (Formula, AdditionFormula, DoublingFormula, DifferentialAdditionFormula,
ScalingFormula, LadderFormula, NegationFormula)
-from .params import DomainParameters
from .naf import naf, wnaf
+from .params import DomainParameters
from .point import Point
+@public
+class ScalarMultiplicationAction(Action):
+ """A scalar multiplication of a point on a curve by a scalar."""
+ point: Point
+ scalar: int
+
+ def __init__(self, point: Point, scalar: int):
+ super().__init__()
+ self.point = point
+ self.scalar = scalar
+
+ def __repr__(self):
+ return f"{self.__class__.__name__}({self.point}, {self.scalar})"
+
+
class ScalarMultiplier(object):
"""
A scalar multiplication algorithm.
@@ -42,9 +57,7 @@ class ScalarMultiplier(object):
return copy(other)
if other == self._group.neutral:
return copy(one)
- return \
- getcontext().execute(self.formulas["add"], one, other, **self._group.curve.parameters)[
- 0]
+ return self.formulas["add"](one, other, **self._group.curve.parameters)[0]
def _dbl(self, point: Point) -> Point:
if "dbl" not in self.formulas:
@@ -52,12 +65,12 @@ class ScalarMultiplier(object):
if self.short_circuit:
if point == self._group.neutral:
return copy(point)
- return getcontext().execute(self.formulas["dbl"], point, **self._group.curve.parameters)[0]
+ return self.formulas["dbl"](point, **self._group.curve.parameters)[0]
def _scl(self, point: Point) -> Point:
if "scl" not in self.formulas:
raise NotImplementedError
- return getcontext().execute(self.formulas["scl"], point, **self._group.curve.parameters)[0]
+ return self.formulas["scl"](point, **self._group.curve.parameters)[0]
def _ladd(self, start: Point, to_dbl: Point, to_add: Point) -> Tuple[Point, ...]:
if "ladd" not in self.formulas:
@@ -67,8 +80,7 @@ class ScalarMultiplier(object):
return to_dbl, to_add
if to_add == self._group.neutral:
return self._dbl(to_dbl), to_dbl
- return getcontext().execute(self.formulas["ladd"], start, to_dbl, to_add,
- **self._group.curve.parameters)
+ return self.formulas["ladd"](start, to_dbl, to_add, **self._group.curve.parameters)
def _dadd(self, start: Point, one: Point, other: Point) -> Point:
if "dadd" not in self.formulas:
@@ -78,13 +90,12 @@ class ScalarMultiplier(object):
return copy(other)
if other == self._group.neutral:
return copy(one)
- return getcontext().execute(self.formulas["dadd"], start, one, other,
- **self._group.curve.parameters)[0]
+ return self.formulas["dadd"](start, one, other, **self._group.curve.parameters)[0]
def _neg(self, point: Point) -> Point:
if "neg" not in self.formulas:
raise NotImplementedError
- return getcontext().execute(self.formulas["neg"], point, **self._group.curve.parameters)[0]
+ return self.formulas["neg"](point, **self._group.curve.parameters)[0]
def init(self, group: DomainParameters, point: Point):
"""Initialize the scalar multiplier with a group and a point."""
@@ -122,25 +133,26 @@ class LTRMultiplier(ScalarMultiplier):
def multiply(self, scalar: int) -> Point:
if not self._initialized:
raise ValueError("ScalaMultiplier not initialized.")
- if scalar == 0:
- return copy(self._group.neutral)
- if self.complete:
- q = self._point
- r = copy(self._group.neutral)
- top = self._group.order.bit_length() - 1
- else:
- q = self._dbl(self._point)
- r = copy(self._point)
- top = scalar.bit_length() - 2
- for i in range(top, -1, -1):
- r = self._dbl(r)
- if scalar & (1 << i) != 0:
- r = self._add(r, q)
- elif self.always:
- self._add(r, q)
- if "scl" in self.formulas:
- r = self._scl(r)
- return r
+ with ScalarMultiplicationAction(self._point, scalar):
+ if scalar == 0:
+ return copy(self._group.neutral)
+ if self.complete:
+ q = self._point
+ r = copy(self._group.neutral)
+ top = self._group.order.bit_length() - 1
+ else:
+ q = self._dbl(self._point)
+ r = copy(self._point)
+ top = scalar.bit_length() - 2
+ for i in range(top, -1, -1):
+ r = self._dbl(r)
+ if scalar & (1 << i) != 0:
+ r = self._add(r, q)
+ elif self.always:
+ self._add(r, q)
+ if "scl" in self.formulas:
+ r = self._scl(r)
+ return r
@public
@@ -162,20 +174,21 @@ class RTLMultiplier(ScalarMultiplier):
def multiply(self, scalar: int) -> Point:
if not self._initialized:
raise ValueError("ScalaMultiplier not initialized.")
- if scalar == 0:
- return copy(self._group.neutral)
- q = self._point
- r = copy(self._group.neutral)
- while scalar > 0:
- if scalar & 1 != 0:
- r = self._add(r, q)
- elif self.always:
- self._add(r, q)
- q = self._dbl(q)
- scalar >>= 1
- if "scl" in self.formulas:
- r = self._scl(r)
- return r
+ with ScalarMultiplicationAction(self._point, scalar):
+ if scalar == 0:
+ return copy(self._group.neutral)
+ q = self._point
+ r = copy(self._group.neutral)
+ while scalar > 0:
+ if scalar & 1 != 0:
+ r = self._add(r, q)
+ elif self.always:
+ self._add(r, q)
+ q = self._dbl(q)
+ scalar >>= 1
+ if "scl" in self.formulas:
+ r = self._scl(r)
+ return r
class CoronMultiplier(ScalarMultiplier):
@@ -196,18 +209,19 @@ class CoronMultiplier(ScalarMultiplier):
def multiply(self, scalar: int) -> Point:
if not self._initialized:
raise ValueError("ScalaMultiplier not initialized.")
- if scalar == 0:
- return copy(self._group.neutral)
- q = self._point
- p0 = copy(q)
- for i in range(scalar.bit_length() - 2, -1, -1):
- p0 = self._dbl(p0)
- p1 = self._add(p0, q)
- if scalar & (1 << i) != 0:
- p0 = p1
- if "scl" in self.formulas:
- p0 = self._scl(p0)
- return p0
+ with ScalarMultiplicationAction(self._point, scalar):
+ if scalar == 0:
+ return copy(self._group.neutral)
+ q = self._point
+ p0 = copy(q)
+ for i in range(scalar.bit_length() - 2, -1, -1):
+ p0 = self._dbl(p0)
+ p1 = self._add(p0, q)
+ if scalar & (1 << i) != 0:
+ p0 = p1
+ if "scl" in self.formulas:
+ p0 = self._scl(p0)
+ return p0
@public
@@ -229,25 +243,26 @@ class LadderMultiplier(ScalarMultiplier):
def multiply(self, scalar: int) -> Point:
if not self._initialized:
raise ValueError("ScalaMultiplier not initialized.")
- if scalar == 0:
- return copy(self._group.neutral)
- q = self._point
- if self.complete:
- p0 = copy(self._group.neutral)
- p1 = self._point
- top = self._group.order.bit_length() - 1
- else:
- p0 = copy(q)
- p1 = self._dbl(q)
- top = scalar.bit_length() - 2
- for i in range(top, -1, -1):
- if scalar & (1 << i) == 0:
- p0, p1 = self._ladd(q, p0, p1)
+ with ScalarMultiplicationAction(self._point, scalar):
+ if scalar == 0:
+ return copy(self._group.neutral)
+ q = self._point
+ if self.complete:
+ p0 = copy(self._group.neutral)
+ p1 = self._point
+ top = self._group.order.bit_length() - 1
else:
- p1, p0 = self._ladd(q, p1, p0)
- if "scl" in self.formulas:
- p0 = self._scl(p0)
- return p0
+ p0 = copy(q)
+ p1 = self._dbl(q)
+ top = scalar.bit_length() - 2
+ for i in range(top, -1, -1):
+ if scalar & (1 << i) == 0:
+ p0, p1 = self._ladd(q, p0, p1)
+ else:
+ p1, p0 = self._ladd(q, p1, p0)
+ if "scl" in self.formulas:
+ p0 = self._scl(p0)
+ return p0
@public
@@ -267,24 +282,25 @@ class SimpleLadderMultiplier(ScalarMultiplier):
def multiply(self, scalar: int) -> Point:
if not self._initialized:
raise ValueError("ScalaMultiplier not initialized.")
- if scalar == 0:
- return copy(self._group.neutral)
- if self.complete:
- top = self._group.order.bit_length() - 1
- else:
- top = scalar.bit_length() - 1
- p0 = copy(self._group.neutral)
- p1 = copy(self._point)
- for i in range(top, -1, -1):
- if scalar & (1 << i) == 0:
- p1 = self._add(p0, p1)
- p0 = self._dbl(p0)
+ with ScalarMultiplicationAction(self._point, scalar):
+ if scalar == 0:
+ return copy(self._group.neutral)
+ if self.complete:
+ top = self._group.order.bit_length() - 1
else:
- p0 = self._add(p0, p1)
- p1 = self._dbl(p1)
- if "scl" in self.formulas:
- p0 = self._scl(p0)
- return p0
+ top = scalar.bit_length() - 1
+ p0 = copy(self._group.neutral)
+ p1 = copy(self._point)
+ for i in range(top, -1, -1):
+ if scalar & (1 << i) == 0:
+ p1 = self._add(p0, p1)
+ p0 = self._dbl(p0)
+ else:
+ p0 = self._add(p0, p1)
+ p1 = self._dbl(p1)
+ if "scl" in self.formulas:
+ p0 = self._scl(p0)
+ return p0
@public
@@ -304,25 +320,26 @@ class DifferentialLadderMultiplier(ScalarMultiplier):
def multiply(self, scalar: int) -> Point:
if not self._initialized:
raise ValueError("ScalaMultiplier not initialized.")
- if scalar == 0:
- return copy(self._group.neutral)
- if self.complete:
- top = self._group.order.bit_length() - 1
- else:
- top = scalar.bit_length() - 1
- q = self._point
- p0 = copy(self._group.neutral)
- p1 = copy(q)
- for i in range(top, -1, -1):
- if scalar & (1 << i) == 0:
- p1 = self._dadd(q, p0, p1)
- p0 = self._dbl(p0)
+ with ScalarMultiplicationAction(self._point, scalar):
+ if scalar == 0:
+ return copy(self._group.neutral)
+ if self.complete:
+ top = self._group.order.bit_length() - 1
else:
- p0 = self._dadd(q, p0, p1)
- p1 = self._dbl(p1)
- if "scl" in self.formulas:
- p0 = self._scl(p0)
- return p0
+ top = scalar.bit_length() - 1
+ q = self._point
+ p0 = copy(self._group.neutral)
+ p1 = copy(q)
+ for i in range(top, -1, -1):
+ if scalar & (1 << i) == 0:
+ p1 = self._dadd(q, p0, p1)
+ p0 = self._dbl(p0)
+ else:
+ p0 = self._dadd(q, p0, p1)
+ p1 = self._dbl(p1)
+ if "scl" in self.formulas:
+ p0 = self._scl(p0)
+ return p0
@public
@@ -343,19 +360,20 @@ class BinaryNAFMultiplier(ScalarMultiplier):
def multiply(self, scalar: int) -> Point:
if not self._initialized:
raise ValueError("ScalaMultiplier not initialized.")
- if scalar == 0:
- return copy(self._group.neutral)
- bnaf = naf(scalar)
- q = copy(self._group.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)
- if "scl" in self.formulas:
- q = self._scl(q)
- return q
+ with ScalarMultiplicationAction(self._point, scalar):
+ if scalar == 0:
+ return copy(self._group.neutral)
+ bnaf = naf(scalar)
+ q = copy(self._group.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)
+ if "scl" in self.formulas:
+ q = self._scl(q)
+ return q
@public
@@ -390,20 +408,21 @@ class WindowNAFMultiplier(ScalarMultiplier):
def multiply(self, scalar: int) -> Point:
if not self._initialized:
raise ValueError("ScalaMultiplier not initialized.")
- if scalar == 0:
- return copy(self._group.neutral)
- naf = wnaf(scalar, self.width)
- q = copy(self._group.neutral)
- for val in naf:
- q = self._dbl(q)
- if val > 0:
- q = self._add(q, self._points[val])
- elif val < 0:
- if self.precompute_negation:
- neg = self._points_neg[-val]
- else:
- neg = self._neg(self._points[-val])
- q = self._add(q, neg)
- if "scl" in self.formulas:
- q = self._scl(q)
- return q
+ with ScalarMultiplicationAction(self._point, scalar):
+ if scalar == 0:
+ return copy(self._group.neutral)
+ naf = wnaf(scalar, self.width)
+ q = copy(self._group.neutral)
+ for val in naf:
+ q = self._dbl(q)
+ if val > 0:
+ q = self._add(q, self._points[val])
+ elif val < 0:
+ if self.precompute_negation:
+ neg = self._points_neg[-val]
+ else:
+ neg = self._neg(self._points[-val])
+ q = self._add(q, neg)
+ if "scl" in self.formulas:
+ q = self._scl(q)
+ return q
diff --git a/pyecsca/ec/op.py b/pyecsca/ec/op.py
index 3bf502e..0685572 100644
--- a/pyecsca/ec/op.py
+++ b/pyecsca/ec/op.py
@@ -6,6 +6,7 @@ from typing import FrozenSet, cast, Any, Optional
from public import public
+from .context import Action
from .mod import Mod
@@ -103,3 +104,13 @@ class CodeOp(object):
loc = dict(kwargs)
exec(self.compiled, {}, loc)
return loc[self.result]
+
+
+@public
+class OperationAction(Action):
+ """An operation."""
+ operation: CodeOp
+
+ def __init__(self, operation: CodeOp):
+ super().__init__()
+ self.operation = operation
diff --git a/pyecsca/ec/point.py b/pyecsca/ec/point.py
index b5ba10b..2e41606 100644
--- a/pyecsca/ec/point.py
+++ b/pyecsca/ec/point.py
@@ -3,12 +3,30 @@ from typing import Mapping, Any
from public import public
-from .coordinates import CoordinateModel, AffineCoordinateModel
+from .context import Action
+from .coordinates import AffineCoordinateModel, CoordinateModel
from .mod import Mod, Undefined
from .op import CodeOp
@public
+class CoordinateMappingAction(Action):
+ """A mapping of a point from one coordinate system to another one, usually one is an affine one."""
+ model_from: CoordinateModel
+ model_to: CoordinateModel
+ point: "Point"
+
+ def __init__(self, model_from: CoordinateModel, model_to: CoordinateModel, point: "Point"):
+ super().__init__()
+ self.model_from = model_from
+ self.model_to = model_to
+ self.point = point
+
+ def __repr__(self):
+ return f"{self.__class__.__name__}(from={self.model_from}, to={self.model_to}, {self.point})"
+
+
+@public
class Point(object):
"""A point with coordinates in a coordinate model."""
coordinate_model: CoordinateModel
@@ -29,44 +47,46 @@ class Point(object):
def to_affine(self) -> "Point":
"""Convert this point into the affine coordinate model, if possible."""
- if isinstance(self.coordinate_model, AffineCoordinateModel):
- return copy(self)
- ops = set()
- for s in self.coordinate_model.satisfying:
- try:
- ops.add(CodeOp(s))
- except Exception:
- pass
affine_model = AffineCoordinateModel(self.coordinate_model.curve_model)
- result_variables = set(map(lambda x: x.result, ops))
- if not result_variables.issuperset(affine_model.variables):
- raise NotImplementedError
- result = {}
- for op in ops:
- if op.result not in affine_model.variables:
- continue
- result[op.result] = op(**self.coords)
- return Point(affine_model, **result)
+ with CoordinateMappingAction(self.coordinate_model, affine_model, self):
+ if isinstance(self.coordinate_model, AffineCoordinateModel):
+ return copy(self)
+ ops = set()
+ for s in self.coordinate_model.satisfying:
+ try:
+ ops.add(CodeOp(s))
+ except Exception:
+ pass
+ result_variables = set(map(lambda x: x.result, ops))
+ if not result_variables.issuperset(affine_model.variables):
+ raise NotImplementedError
+ result = {}
+ for op in ops:
+ if op.result not in affine_model.variables:
+ continue
+ result[op.result] = op(**self.coords)
+ return Point(affine_model, **result)
@staticmethod
def from_affine(coordinate_model: CoordinateModel, affine_point: "Point") -> "Point":
"""Convert an affine point into a given coordinate model, if possible."""
- if not isinstance(affine_point.coordinate_model, AffineCoordinateModel):
- raise ValueError
- result = {}
- n = affine_point.coords["x"].n
- for var in coordinate_model.variables: #  XXX: This just works for the stuff currently in EFD.
- if var == "X":
- result[var] = affine_point.coords["x"]
- elif var == "Y":
- result[var] = affine_point.coords["y"]
- elif var.startswith("Z"):
- result[var] = Mod(1, n)
- elif var == "T":
- result[var] = Mod(affine_point.coords["x"] * affine_point.coords["y"], n)
- else:
- raise NotImplementedError
- return Point(coordinate_model, **result)
+ with CoordinateMappingAction(affine_point.coordinate_model, coordinate_model, affine_point):
+ if not isinstance(affine_point.coordinate_model, AffineCoordinateModel):
+ raise ValueError
+ result = {}
+ n = affine_point.coords["x"].n
+ for var in coordinate_model.variables: #  XXX: This just works for the stuff currently in EFD.
+ if var == "X":
+ result[var] = affine_point.coords["x"]
+ elif var == "Y":
+ result[var] = affine_point.coords["y"]
+ elif var.startswith("Z"):
+ result[var] = Mod(1, n)
+ elif var == "T":
+ result[var] = Mod(affine_point.coords["x"] * affine_point.coords["y"], n)
+ else:
+ raise NotImplementedError
+ return Point(coordinate_model, **result)
def equals(self, other: Any) -> bool:
"""Test whether this point is equal to `other` irrespective of the coordinate model (in the affine sense)."""
diff --git a/pyecsca/ec/signature.py b/pyecsca/ec/signature.py
index 90ed9fb..569367e 100644
--- a/pyecsca/ec/signature.py
+++ b/pyecsca/ec/signature.py
@@ -5,11 +5,11 @@ from typing import Optional, Any
from asn1crypto.core import Sequence, SequenceOf, Integer
from public import public
-from .context import getcontext
+from .context import Action
from .formula import AdditionFormula
-from .params import DomainParameters
from .mod import Mod
from .mult import ScalarMultiplier
+from .params import DomainParameters
from .point import Point
@@ -20,7 +20,7 @@ class SignatureResult(object):
s: int
def __init__(self, r: int, s: int, data: Optional[bytes] = None, digest: Optional[bytes] = None,
- nonce: Optional[int] = None, privkey: Optional[int] = None,
+ nonce: Optional[int] = None, privkey: Optional[Mod] = None,
pubkey: Optional[Point] = None):
self.r = r
self.s = s
@@ -51,17 +51,66 @@ class SignatureResult(object):
@public
+class ECDSAAction(Action):
+ """An ECDSA action base class."""
+ params: DomainParameters
+ hash_algo: Optional[Any]
+ msg: bytes
+
+ def __init__(self, params: DomainParameters, hash_algo: Optional[Any],
+ msg: bytes):
+ super().__init__()
+ self.params = params
+ self.hash_algo = hash_algo
+ self.msg = msg
+
+ def __repr__(self):
+ return f"{self.__class__.__name__}({self.params}, {self.hash_algo}, {self.msg})"
+
+
+@public
+class ECDSASignAction(ECDSAAction):
+ """An ECDSA signing."""
+ privkey: Mod
+
+ def __init__(self, params: DomainParameters, hash_algo: Optional[Any], msg: bytes,
+ privkey: Mod):
+ super().__init__(params, hash_algo, msg)
+ self.privkey = privkey
+
+ def __repr__(self):
+ return f"{self.__class__.__name__}({self.params}, {self.hash_algo}, {self.msg}, {self.privkey})"
+
+
+@public
+class ECDSAVerifyAction(ECDSAAction):
+ """An ECDSA verification."""
+ signature: SignatureResult
+ pubkey: Point
+
+ def __init__(self, params: DomainParameters, hash_algo: Optional[Any], msg: bytes,
+ signature: SignatureResult, pubkey: Point):
+ super().__init__(params, hash_algo, msg)
+ self.signature = signature
+ self.pubkey = pubkey
+
+ def __repr__(self):
+ return f"{self.__class__.__name__}({self.params}, {self.hash_algo}, {self.msg}, {self.signature}, {self.pubkey})"
+
+
+@public
class Signature(object):
"""An EC based signature primitive. (ECDSA)"""
mult: ScalarMultiplier
- group: DomainParameters
+ params: DomainParameters
add: Optional[AdditionFormula]
pubkey: Optional[Point]
- privkey: Optional[int]
+ privkey: Optional[Mod]
hash_algo: Optional[Any]
- def __init__(self, mult: ScalarMultiplier, group: DomainParameters, add: Optional[AdditionFormula] = None,
- pubkey: Optional[Point] = None, privkey: Optional[int] = None,
+ def __init__(self, mult: ScalarMultiplier, params: DomainParameters,
+ add: Optional[AdditionFormula] = None,
+ pubkey: Optional[Point] = None, privkey: Optional[Mod] = None,
hash_algo: Optional[Any] = None):
if pubkey is None and privkey is None:
raise ValueError
@@ -71,7 +120,7 @@ class Signature(object):
elif isinstance(mult.formulas["add"], AdditionFormula):
add = mult.formulas["add"]
self.mult = mult
- self.group = group
+ self.params = params
self.add = add
self.pubkey = pubkey
self.privkey = privkey
@@ -89,19 +138,19 @@ class Signature(object):
def _get_nonce(self, nonce: Optional[int]) -> Mod:
if nonce is None:
- return Mod(secrets.randbelow(self.group.order), self.group.order)
+ return Mod.random(self.params.order)
else:
- return Mod(nonce, self.group.order)
+ return Mod(nonce, self.params.order)
def _do_sign(self, nonce: Mod, digest: bytes) -> SignatureResult:
z = int.from_bytes(digest, byteorder="big")
- if len(digest) * 8 > self.group.order.bit_length():
- z >>= len(digest) * 8 - self.group.order.bit_length()
- self.mult.init(self.group, self.group.generator)
+ if len(digest) * 8 > self.params.order.bit_length():
+ z >>= len(digest) * 8 - self.params.order.bit_length()
+ self.mult.init(self.params, self.params.generator)
point = self.mult.multiply(int(nonce))
- affine_point = point.to_affine() #  TODO: add to context
- r = Mod(int(affine_point.x), self.group.order)
- s = nonce.inverse() * (Mod(z, self.group.order) + r * self.privkey)
+ affine_point = point.to_affine()
+ r = Mod(int(affine_point.x), self.params.order)
+ s = nonce.inverse() * (Mod(z, self.params.order) + r * self.privkey)
return SignatureResult(int(r), int(s), digest=digest, nonce=int(nonce),
privkey=self.privkey)
@@ -116,29 +165,30 @@ class Signature(object):
"""Sign data."""
if not self.can_sign:
raise RuntimeError("This instance cannot sign.")
- k = self._get_nonce(nonce)
- if self.hash_algo is None:
- digest = data
- else:
- digest = self.hash_algo(data).digest()
- return self._do_sign(k, digest)
+ with ECDSASignAction(self.params, self.hash_algo, data, self.privkey):
+ k = self._get_nonce(nonce)
+ if self.hash_algo is None:
+ digest = data
+ else:
+ digest = self.hash_algo(data).digest()
+ return self._do_sign(k, digest)
def _do_verify(self, signature: SignatureResult, digest: bytes) -> bool:
- if self.pubkey is None:
+ if self.pubkey is None or self.add is None:
return False
z = int.from_bytes(digest, byteorder="big")
- if len(digest) * 8 > self.group.order.bit_length():
- z >>= len(digest) * 8 - self.group.order.bit_length()
- c = Mod(signature.s, self.group.order).inverse()
- u1 = Mod(z, self.group.order) * c
- u2 = Mod(signature.r, self.group.order) * c
- self.mult.init(self.group, self.group.generator)
+ if len(digest) * 8 > self.params.order.bit_length():
+ z >>= len(digest) * 8 - self.params.order.bit_length()
+ c = Mod(signature.s, self.params.order).inverse()
+ u1 = Mod(z, self.params.order) * c
+ u2 = Mod(signature.r, self.params.order) * c
+ self.mult.init(self.params, self.params.generator)
p1 = self.mult.multiply(int(u1))
- self.mult.init(self.group, self.pubkey)
+ self.mult.init(self.params, self.pubkey)
p2 = self.mult.multiply(int(u2))
- p = getcontext().execute(self.add, p1, p2, **self.group.curve.parameters)[0]
- affine = p.to_affine() # TODO: add to context
- v = Mod(int(affine.x), self.group.order)
+ p = self.add(p1, p2, **self.params.curve.parameters)[0]
+ affine = p.to_affine()
+ v = Mod(int(affine.x), self.params.order)
return signature.r == int(v)
def verify_hash(self, signature: SignatureResult, digest: bytes) -> bool:
@@ -151,62 +201,69 @@ class Signature(object):
"""Verify data."""
if not self.can_verify:
raise RuntimeError("This instance cannot verify.")
- if self.hash_algo is None:
- digest = data
- else:
- digest = self.hash_algo(data).digest()
- return self._do_verify(signature, digest)
+ with ECDSAVerifyAction(self.params, self.hash_algo, data, signature, self.pubkey):
+ if self.hash_algo is None:
+ digest = data
+ else:
+ digest = self.hash_algo(data).digest()
+ return self._do_verify(signature, digest)
@public
class ECDSA_NONE(Signature):
"""ECDSA with raw message input."""
- def __init__(self, mult: ScalarMultiplier, group: DomainParameters, add: Optional[AdditionFormula] = None,
- pubkey: Optional[Point] = None, privkey: Optional[int] = None):
- super().__init__(mult, group, add, pubkey, privkey)
+ def __init__(self, mult: ScalarMultiplier, params: DomainParameters,
+ add: Optional[AdditionFormula] = None,
+ pubkey: Optional[Point] = None, privkey: Optional[Mod] = None):
+ super().__init__(mult, params, add, pubkey, privkey)
@public
class ECDSA_SHA1(Signature):
"""ECDSA with SHA1."""
- def __init__(self, mult: ScalarMultiplier, group: DomainParameters, add: Optional[AdditionFormula] = None,
- pubkey: Optional[Point] = None, privkey: Optional[int] = None):
- super().__init__(mult, group, add, pubkey, privkey, hashlib.sha1)
+ def __init__(self, mult: ScalarMultiplier, params: DomainParameters,
+ add: Optional[AdditionFormula] = None,
+ pubkey: Optional[Point] = None, privkey: Optional[Mod] = None):
+ super().__init__(mult, params, add, pubkey, privkey, hashlib.sha1)
@public
class ECDSA_SHA224(Signature):
"""ECDSA with SHA224."""
- def __init__(self, mult: ScalarMultiplier, group: DomainParameters, add: Optional[AdditionFormula] = None,
- pubkey: Optional[Point] = None, privkey: Optional[int] = None):
- super().__init__(mult, group, add, pubkey, privkey, hashlib.sha224)
+ def __init__(self, mult: ScalarMultiplier, params: DomainParameters,
+ add: Optional[AdditionFormula] = None,
+ pubkey: Optional[Point] = None, privkey: Optional[Mod] = None):
+ super().__init__(mult, params, add, pubkey, privkey, hashlib.sha224)
@public
class ECDSA_SHA256(Signature):
"""ECDSA with SHA256."""
- def __init__(self, mult: ScalarMultiplier, group: DomainParameters, add: Optional[AdditionFormula] = None,
- pubkey: Optional[Point] = None, privkey: Optional[int] = None):
- super().__init__(mult, group, add, pubkey, privkey, hashlib.sha256)
+ def __init__(self, mult: ScalarMultiplier, params: DomainParameters,
+ add: Optional[AdditionFormula] = None,
+ pubkey: Optional[Point] = None, privkey: Optional[Mod] = None):
+ super().__init__(mult, params, add, pubkey, privkey, hashlib.sha256)
@public
class ECDSA_SHA384(Signature):
"""ECDSA with SHA384."""
- def __init__(self, mult: ScalarMultiplier, group: DomainParameters, add: Optional[AdditionFormula] = None,
- pubkey: Optional[Point] = None, privkey: Optional[int] = None):
- super().__init__(mult, group, add, pubkey, privkey, hashlib.sha384)
+ def __init__(self, mult: ScalarMultiplier, params: DomainParameters,
+ add: Optional[AdditionFormula] = None,
+ pubkey: Optional[Point] = None, privkey: Optional[Mod] = None):
+ super().__init__(mult, params, add, pubkey, privkey, hashlib.sha384)
@public
class ECDSA_SHA512(Signature):
"""ECDSA with SHA512."""
- def __init__(self, mult: ScalarMultiplier, group: DomainParameters, add: Optional[AdditionFormula] = None,
- pubkey: Optional[Point] = None, privkey: Optional[int] = None):
- super().__init__(mult, group, add, pubkey, privkey, hashlib.sha512)
+ def __init__(self, mult: ScalarMultiplier, params: DomainParameters,
+ add: Optional[AdditionFormula] = None,
+ pubkey: Optional[Point] = None, privkey: Optional[Mod] = None):
+ super().__init__(mult, params, add, pubkey, privkey, hashlib.sha512)
diff --git a/test/ec/test_context.py b/test/ec/test_context.py
index 6b9c526..3f46c9d 100644
--- a/test/ec/test_context.py
+++ b/test/ec/test_context.py
@@ -1,22 +1,9 @@
-import ast
from unittest import TestCase
-from pyecsca.ec.context import (local, DefaultContext, OpResult, NullContext, getcontext,
- setcontext,
- resetcontext)
-from pyecsca.ec.coordinates import AffineCoordinateModel
+from pyecsca.ec.context import (local, DefaultContext, NullContext, getcontext,
+ setcontext, resetcontext)
from pyecsca.ec.curves import get_params
-from pyecsca.ec.mod import Mod
-from pyecsca.ec.mult import LTRMultiplier
-from pyecsca.ec.point import Point
-
-
-class OpResultTests(TestCase):
-
- def test_str(self):
- for op, char in zip((ast.Add(), ast.Sub(), ast.Mult(), ast.Div()), "+-*/"):
- res = OpResult("X1", Mod(0, 5), op, Mod(2, 5), Mod(3, 5))
- self.assertEqual(str(res), "X1")
+from pyecsca.ec.mult import LTRMultiplier, ScalarMultiplicationAction
class ContextTests(TestCase):
@@ -40,17 +27,10 @@ class ContextTests(TestCase):
with local(DefaultContext()) as ctx:
self.mult.multiply(59)
- self.assertEqual(len(ctx.actions), 10)
+ self.assertEqual(len(ctx.actions), 1)
+ self.assertIsInstance(next(iter(ctx.actions.keys())), ScalarMultiplicationAction)
self.assertEqual(len(getcontext().actions), 0)
- def test_execute(self):
- with self.assertRaises(ValueError):
- getcontext().execute(self.coords.formulas["z"], self.base, self.base)
- with self.assertRaises(ValueError):
- getcontext().execute(self.coords.formulas["z"],
- Point(AffineCoordinateModel(self.secp128r1.curve.model),
- x=Mod(1, 5), y=Mod(2, 5)))
-
def test_str(self):
with local(DefaultContext()) as default:
self.mult.multiply(59)
diff --git a/test/ec/test_formula.py b/test/ec/test_formula.py
new file mode 100644
index 0000000..cedbe1f
--- /dev/null
+++ b/test/ec/test_formula.py
@@ -0,0 +1,35 @@
+from unittest import TestCase
+
+from pyecsca.ec.curves import get_params
+from pyecsca.ec.key_generation import KeyGeneration
+from pyecsca.ec.mult import LTRMultiplier
+
+
+class FormulaTests(TestCase):
+
+ def setUp(self):
+ self.secp128r1 = get_params("secg", "secp128r1", "projective")
+ self.add = self.secp128r1.curve.coordinate_model.formulas["add-2007-bl"]
+
+ def test_wrong_call(self):
+ with self.assertRaises(ValueError):
+ self.add()
+ with self.assertRaises(ValueError):
+ self.add(self.secp128r1.generator.to_affine(), self.secp128r1.generator.to_affine())
+
+ def test_indices(self):
+ self.assertEqual(self.add.input_index, 1)
+ self.assertEqual(self.add.output_index, 3)
+
+ def test_inputs_outputs(self):
+ self.assertEqual(self.add.inputs, {"X1", "Y1", "Z1", "X2", "Y2", "Z2"})
+ self.assertEqual(self.add.outputs, {"X3", "Y3", "Z3"})
+
+ def test_num_ops(self):
+ self.assertEqual(self.add.num_operations, 33)
+ self.assertEqual(self.add.num_multiplications, 17)
+ self.assertEqual(self.add.num_divisions, 0)
+ self.assertEqual(self.add.num_inversions, 0)
+ self.assertEqual(self.add.num_powers, 0)
+ self.assertEqual(self.add.num_squarings, 6)
+ self.assertEqual(self.add.num_addsubs, 10) \ No newline at end of file
diff --git a/test/ec/test_key_agreement.py b/test/ec/test_key_agreement.py
index b771863..adffbab 100644
--- a/test/ec/test_key_agreement.py
+++ b/test/ec/test_key_agreement.py
@@ -3,7 +3,9 @@ from unittest import TestCase
from parameterized import parameterized
from pyecsca.ec.curves import get_params
-from pyecsca.ec.key_agreement import *
+from pyecsca.ec.key_agreement import (ECDH_NONE, ECDH_SHA1, ECDH_SHA224, ECDH_SHA256, ECDH_SHA384,
+ ECDH_SHA512)
+from pyecsca.ec.mod import Mod
from pyecsca.ec.mult import LTRMultiplier
@@ -14,11 +16,11 @@ class KeyAgreementTests(TestCase):
self.add = self.secp128r1.curve.coordinate_model.formulas["add-2007-bl"]
self.dbl = self.secp128r1.curve.coordinate_model.formulas["dbl-2007-bl"]
self.mult = LTRMultiplier(self.add, self.dbl)
- self.priv_a = 0xdeadbeef
+ self.priv_a = Mod(0xdeadbeef, self.secp128r1.order)
self.mult.init(self.secp128r1, self.secp128r1.generator)
- self.pub_a = self.mult.multiply(self.priv_a)
- self.priv_b = 0xcafebabe
- self.pub_b = self.mult.multiply(self.priv_b)
+ self.pub_a = self.mult.multiply(int(self.priv_a))
+ self.priv_b = Mod(0xcafebabe, self.secp128r1.order)
+ self.pub_b = self.mult.multiply(int(self.priv_b))
@parameterized.expand([
("NONE", ECDH_NONE),
diff --git a/test/ec/test_key_generation.py b/test/ec/test_key_generation.py
new file mode 100644
index 0000000..59f3b23
--- /dev/null
+++ b/test/ec/test_key_generation.py
@@ -0,0 +1,26 @@
+from unittest import TestCase
+
+from pyecsca.ec.curves import get_params
+from pyecsca.ec.key_generation import KeyGeneration
+from pyecsca.ec.mult import LTRMultiplier
+
+
+class KeyGenerationTests(TestCase):
+
+ def setUp(self):
+ self.secp128r1 = get_params("secg", "secp128r1", "projective")
+ self.add = self.secp128r1.curve.coordinate_model.formulas["add-2007-bl"]
+ self.dbl = self.secp128r1.curve.coordinate_model.formulas["dbl-2007-bl"]
+ self.mult = LTRMultiplier(self.add, self.dbl)
+
+ def test_basic(self):
+ generator = KeyGeneration(self.mult, self.secp128r1)
+ priv, pub = generator.generate()
+ self.assertIsNotNone(priv)
+ self.assertIsNotNone(pub)
+ self.assertTrue(self.secp128r1.curve.is_on_curve(pub))
+ generator = KeyGeneration(self.mult, self.secp128r1, True)
+ priv, pub = generator.generate()
+ self.assertIsNotNone(priv)
+ self.assertIsNotNone(pub)
+ self.assertTrue(self.secp128r1.curve.is_on_curve(pub))
diff --git a/test/ec/test_op.py b/test/ec/test_op.py
index a7f9cd0..9471148 100644
--- a/test/ec/test_op.py
+++ b/test/ec/test_op.py
@@ -1,8 +1,10 @@
+import ast
from ast import parse
from unittest import TestCase
from parameterized import parameterized
+from pyecsca.ec.formula import OpResult
from pyecsca.ec.mod import Mod
from pyecsca.ec.op import CodeOp, OpType
@@ -35,3 +37,10 @@ class OpTests(TestCase):
op = CodeOp(code)
res = op(**locals)
self.assertEqual(res, result)
+
+class OpResultTests(TestCase):
+
+ def test_str(self):
+ for op, char in zip((ast.Add(), ast.Sub(), ast.Mult(), ast.Div()), "+-*/"):
+ res = OpResult("X1", Mod(0, 5), op, Mod(2, 5), Mod(3, 5))
+ self.assertEqual(str(res), "X1") \ No newline at end of file
diff --git a/test/ec/test_signature.py b/test/ec/test_signature.py
index 41e9df2..125c280 100644
--- a/test/ec/test_signature.py
+++ b/test/ec/test_signature.py
@@ -1,10 +1,13 @@
-from hashlib import sha1
from unittest import TestCase
+from parameterized import parameterized
+
from pyecsca.ec.curves import get_params
+from pyecsca.ec.mod import Mod
from pyecsca.ec.mult import LTRMultiplier
-from pyecsca.ec.signature import *
-from parameterized import parameterized
+from pyecsca.ec.signature import (Signature, SignatureResult, ECDSA_NONE, ECDSA_SHA1, ECDSA_SHA224,
+ ECDSA_SHA256, ECDSA_SHA384, ECDSA_SHA512)
+
class SignatureTests(TestCase):
@@ -14,9 +17,9 @@ class SignatureTests(TestCase):
self.dbl = self.secp128r1.curve.coordinate_model.formulas["dbl-2007-bl"]
self.mult = LTRMultiplier(self.add, self.dbl)
self.msg = 0xcafebabe.to_bytes(4, byteorder="big")
- self.priv = 0xdeadbeef
+ self.priv = Mod(0xdeadbeef, self.secp128r1.order)
self.mult.init(self.secp128r1, self.secp128r1.generator)
- self.pub = self.mult.multiply(self.priv)
+ self.pub = self.mult.multiply(self.priv.x)
@parameterized.expand([
("SHA1", ECDSA_SHA1),