1from hashlib import sha256
2from .signature import Signature
3from .math import Math
4from .utils.integer import RandomInteger
5from .utils.binary import numberFromByteString
6from .utils.compatibility import *
7
8
9class Ecdsa:
10
11 @classmethod
12 def sign(cls, message, privateKey, hashfunc=sha256):
13 curve = privateKey.curve
14 byteMessage = hashfunc(toBytes(message)).digest()
15 numberMessage = numberFromByteString(byteMessage, curve.nBitLength)
16
17 r, s, randSignPoint = 0, 0, None
18 kIterator = RandomInteger.rfc6979(byteMessage, privateKey.secret, curve, hashfunc)
19 while r == 0 or s == 0:
20 randNum = next(kIterator)
21 randSignPoint = Math.multiplyGenerator(curve, randNum)
22 r = randSignPoint.x % curve.N
23 s = ((numberMessage + r * privateKey.secret) * (Math.inv(randNum, curve.N))) % curve.N
24 recoveryId = randSignPoint.y & 1
25 if randSignPoint.x >= curve.N:
26 recoveryId += 2
27 if s > curve.N // 2:
28 s = curve.N - s
29 recoveryId ^= 1
30
31 return Signature(r=r, s=s, recoveryId=recoveryId)
32
33 @classmethod
34 def verify(cls, message, signature, publicKey, hashfunc=sha256):
35 curve = publicKey.curve
36 byteMessage = hashfunc(toBytes(message)).digest()
37 numberMessage = numberFromByteString(byteMessage, curve.nBitLength)
38 r = signature.r
39 s = signature.s
40
41 if not 1 <= r <= curve.N - 1:
42 return False
43 if not 1 <= s <= curve.N - 1:
44 return False
45 if not curve.contains(publicKey.point):
46 return False
47 inv = Math.inv(s, curve.N)
48 v = Math.multiplyAndAdd(
49 curve.G, (numberMessage * inv) % curve.N,
50 publicKey.point, (r * inv) % curve.N,
51 curve=curve,
52 )
53 if v.isAtInfinity():
54 return False
55 return v.x % curve.N == r