aboutsummaryrefslogtreecommitdiffhomepage
path: root/pyecsca/ec
diff options
context:
space:
mode:
authorJ08nY2019-04-22 18:04:44 +0200
committerJ08nY2019-04-22 18:04:44 +0200
commit24b9b3958a54bdf7f18df62ae14199749934e3c2 (patch)
treea758d0289337f6aee7540dca4a94ea31ced69217 /pyecsca/ec
parent037194fd8cfe50aa2367c2f3c7fae5b41e7b46f9 (diff)
downloadpyecsca-24b9b3958a54bdf7f18df62ae14199749934e3c2.tar.gz
pyecsca-24b9b3958a54bdf7f18df62ae14199749934e3c2.tar.zst
pyecsca-24b9b3958a54bdf7f18df62ae14199749934e3c2.zip
Add ECDH and ECDSA simulation.
Diffstat (limited to 'pyecsca/ec')
-rw-r--r--pyecsca/ec/group.py10
-rw-r--r--pyecsca/ec/key_agreement.py69
-rw-r--r--pyecsca/ec/point.py9
-rw-r--r--pyecsca/ec/signature.py171
4 files changed, 252 insertions, 7 deletions
diff --git a/pyecsca/ec/group.py b/pyecsca/ec/group.py
index 4c471cc..af0dae2 100644
--- a/pyecsca/ec/group.py
+++ b/pyecsca/ec/group.py
@@ -1,5 +1,3 @@
-from typing import Optional
-
from public import public
from .curve import EllipticCurve
@@ -11,11 +9,11 @@ class AbelianGroup(object):
curve: EllipticCurve
generator: Point
neutral: Point
- order: Optional[int]
- cofactor: Optional[int]
+ order: int
+ cofactor: int
- def __init__(self, curve: EllipticCurve, generator: Point, neutral: Point, order: int = None,
- cofactor: int = None):
+ def __init__(self, curve: EllipticCurve, generator: Point, neutral: Point, order: int,
+ cofactor: int):
self.curve = curve
self.generator = generator
self.neutral = neutral
diff --git a/pyecsca/ec/key_agreement.py b/pyecsca/ec/key_agreement.py
new file mode 100644
index 0000000..5bd2edd
--- /dev/null
+++ b/pyecsca/ec/key_agreement.py
@@ -0,0 +1,69 @@
+import hashlib
+from typing import Optional, Any
+
+from public import public
+
+from .mult import ScalarMultiplier
+from .point import Point
+
+
+@public
+class KeyAgreement(object):
+ mult: ScalarMultiplier
+ pubkey: Point
+ privkey: int
+ hash_algo: Optional[Any]
+
+ def __init__(self, mult: ScalarMultiplier, pubkey: Point, privkey: int,
+ hash_algo: Optional[Any] = None):
+ self.mult = mult
+ self.pubkey = pubkey
+ self.privkey = privkey
+ self.hash_algo = hash_algo
+
+ def perform(self):
+ point = self.mult.multiply(self.privkey, self.pubkey)
+ affine_point = point.to_affine() # TODO: This conversion should be somehow added to the context
+ x = int(affine_point.x)
+ p = self.mult.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
+
+
+@public
+class ECDH_NONE(KeyAgreement):
+ def __init__(self, mult: ScalarMultiplier, pubkey: Point, privkey: int):
+ super().__init__(mult, pubkey, privkey)
+
+
+@public
+class ECDH_SHA1(KeyAgreement):
+ def __init__(self, mult: ScalarMultiplier, pubkey: Point, privkey: int):
+ super().__init__(mult, pubkey, privkey, hashlib.sha1)
+
+
+@public
+class ECDH_SHA224(KeyAgreement):
+ def __init__(self, mult: ScalarMultiplier, pubkey: Point, privkey: int):
+ super().__init__(mult, pubkey, privkey, hashlib.sha224)
+
+
+@public
+class ECDH_SHA256(KeyAgreement):
+ def __init__(self, mult: ScalarMultiplier, pubkey: Point, privkey: int):
+ super().__init__(mult, pubkey, privkey, hashlib.sha256)
+
+
+@public
+class ECDH_SHA384(KeyAgreement):
+ def __init__(self, mult: ScalarMultiplier, pubkey: Point, privkey: int):
+ super().__init__(mult, pubkey, privkey, hashlib.sha384)
+
+
+@public
+class ECDH_SHA512(KeyAgreement):
+ def __init__(self, mult: ScalarMultiplier, pubkey: Point, privkey: int):
+ super().__init__(mult, pubkey, privkey, hashlib.sha512)
diff --git a/pyecsca/ec/point.py b/pyecsca/ec/point.py
index b11c3f1..75b4a1e 100644
--- a/pyecsca/ec/point.py
+++ b/pyecsca/ec/point.py
@@ -1,5 +1,5 @@
from copy import copy
-from typing import Mapping
+from typing import Mapping,Any
from public import public
@@ -19,6 +19,13 @@ class Point(object):
self.coordinate_model = model
self.coords = coords
+ def __getattribute__(self, name: Any):
+ if "coords" in super().__getattribute__("__dict__"):
+ coords = super().__getattribute__("coords")
+ if name in coords:
+ return coords[name]
+ return super().__getattribute__(name)
+
def to_affine(self):
if isinstance(self.coordinate_model, AffineCoordinateModel):
return copy(self)
diff --git a/pyecsca/ec/signature.py b/pyecsca/ec/signature.py
new file mode 100644
index 0000000..147bf27
--- /dev/null
+++ b/pyecsca/ec/signature.py
@@ -0,0 +1,171 @@
+import hashlib
+import secrets
+from typing import Optional, Any
+
+from asn1crypto.core import Sequence, SequenceOf, Integer
+from public import public
+
+from .formula import AdditionFormula
+from .mod import Mod
+from .mult import ScalarMultiplier
+from .point import Point
+
+
+@public
+class SignatureResult(object):
+ r: int
+ 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,
+ pubkey: Optional[Point] = None):
+ self.r = r
+ self.s = s
+
+ @staticmethod
+ def from_DER(data: bytes):
+ r, s = Sequence.load(data).native.values()
+ return SignatureResult(r, s)
+
+ def to_DER(self) -> bytes:
+ obj = SequenceOf(spec=Integer)
+ obj.append(self.r)
+ obj.append(self.s)
+ return obj.dump()
+
+ def __eq__(self, other):
+ if not isinstance(other, SignatureResult):
+ return False
+ return self.r == other.r and self.s == other.s
+
+ def __ne__(self, other):
+ return not self == other
+
+ def __str__(self):
+ return f"(r={self.r}, s={self.s})"
+
+ def __repr__(self):
+ return f"SignatureResult(r={self.r}, s={self.s})"
+
+
+@public
+class Signature(object):
+ mult: ScalarMultiplier
+ add: Optional[AdditionFormula]
+ pubkey: Optional[Point]
+ privkey: Optional[int]
+ hash_algo: Optional[Any]
+
+ def __init__(self, mult: ScalarMultiplier, add: Optional[AdditionFormula] = None,
+ pubkey: Optional[Point] = None, privkey: Optional[int] = None,
+ hash_algo: Optional[Any] = None):
+ if pubkey is None and privkey is None:
+ raise ValueError
+ if add is None:
+ if "add" not in mult.formulas:
+ raise ValueError
+ else:
+ add = mult.formulas["add"]
+ self.mult = mult
+ self.add = add
+ self.pubkey = pubkey
+ self.privkey = privkey
+ self.hash_algo = hash_algo
+
+ @property
+ def can_sign(self) -> bool:
+ return self.privkey is not None
+
+ @property
+ def can_verify(self) -> bool:
+ return self.pubkey is not None
+
+ def _get_nonce(self, nonce: Optional[int]) -> Mod:
+ if nonce is None:
+ return Mod(secrets.randbelow(self.mult.group.order), self.mult.group.order)
+ else:
+ return Mod(nonce, self.mult.group.order)
+
+ def _do_sign(self, nonce: Mod, digest: bytes) -> SignatureResult:
+ point = self.mult.multiply(int(nonce), self.mult.group.generator)
+ affine_point = point.to_affine() #  TODO: add to context
+ r = Mod(int(affine_point.x), self.mult.group.order)
+ s = nonce.inverse() * (Mod(int.from_bytes(digest, byteorder="big"),
+ self.mult.group.order) + r * self.privkey)
+ return SignatureResult(int(r), int(s), digest=digest, nonce=int(nonce),
+ privkey=self.privkey)
+
+ def sign_hash(self, digest: bytes, nonce: Optional[int] = None) -> SignatureResult:
+ k = self._get_nonce(nonce)
+ return self._do_sign(k, digest)
+
+ def sign_data(self, data: bytes, nonce: Optional[int] = None) -> SignatureResult:
+ 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, e: int) -> bool:
+ c = Mod(signature.s, self.mult.group.order).inverse()
+ u1 = Mod(e, self.mult.group.order) * c
+ u2 = Mod(signature.r, self.mult.group.order) * c
+ p1 = self.mult.multiply(int(u1), self.mult.group.generator)
+ p2 = self.mult.multiply(int(u2), self.pubkey)
+ p = self.mult.context.execute(self.add, p1, p2, **self.mult.group.curve.parameters)[0]
+ affine = p.to_affine() # TODO: add to context
+ v = Mod(int(affine.x), self.mult.group.order)
+ return signature.r == int(v)
+
+ def verify_hash(self, signature: SignatureResult, digest: bytes) -> bool:
+ return self._do_verify(signature, int.from_bytes(digest, byteorder="big"))
+
+ def verify_data(self, signature: SignatureResult, data: bytes) -> bool:
+ if self.hash_algo is None:
+ digest = data
+ else:
+ digest = self.hash_algo(data).digest()
+ return self._do_verify(signature, int.from_bytes(digest, byteorder="big"))
+
+
+@public
+class ECDSA_NONE(Signature):
+ def __init__(self, mult: ScalarMultiplier, add: Optional[AdditionFormula] = None,
+ pubkey: Optional[Point] = None, privkey: Optional[int] = None):
+ super().__init__(mult, add, pubkey, privkey)
+
+
+@public
+class ECDSA_SHA1(Signature):
+ def __init__(self, mult: ScalarMultiplier, add: Optional[AdditionFormula] = None,
+ pubkey: Optional[Point] = None, privkey: Optional[int] = None):
+ super().__init__(mult, add, pubkey, privkey, hashlib.sha1)
+
+
+@public
+class ECDSA_SHA224(Signature):
+ def __init__(self, mult: ScalarMultiplier, add: Optional[AdditionFormula] = None,
+ pubkey: Optional[Point] = None, privkey: Optional[int] = None):
+ super().__init__(mult, add, pubkey, privkey, hashlib.sha224)
+
+
+@public
+class ECDSA_SHA256(Signature):
+ def __init__(self, mult: ScalarMultiplier, add: Optional[AdditionFormula] = None,
+ pubkey: Optional[Point] = None, privkey: Optional[int] = None):
+ super().__init__(mult, add, pubkey, privkey, hashlib.sha256)
+
+
+@public
+class ECDSA_SHA384(Signature):
+ def __init__(self, mult: ScalarMultiplier, add: Optional[AdditionFormula] = None,
+ pubkey: Optional[Point] = None, privkey: Optional[int] = None):
+ super().__init__(mult, add, pubkey, privkey, hashlib.sha384)
+
+
+@public
+class ECDSA_SHA512(Signature):
+ def __init__(self, mult: ScalarMultiplier, add: Optional[AdditionFormula] = None,
+ pubkey: Optional[Point] = None, privkey: Optional[int] = None):
+ super().__init__(mult, add, pubkey, privkey, hashlib.sha512)