diff options
| author | J08nY | 2019-04-22 18:04:44 +0200 |
|---|---|---|
| committer | J08nY | 2019-04-22 18:04:44 +0200 |
| commit | 24b9b3958a54bdf7f18df62ae14199749934e3c2 (patch) | |
| tree | a758d0289337f6aee7540dca4a94ea31ced69217 /pyecsca/ec | |
| parent | 037194fd8cfe50aa2367c2f3c7fae5b41e7b46f9 (diff) | |
| download | pyecsca-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.py | 10 | ||||
| -rw-r--r-- | pyecsca/ec/key_agreement.py | 69 | ||||
| -rw-r--r-- | pyecsca/ec/point.py | 9 | ||||
| -rw-r--r-- | pyecsca/ec/signature.py | 171 |
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) |
