aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/ellipticcurve
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/ellipticcurve')
-rw-r--r--.venv/lib/python3.12/site-packages/ellipticcurve/__init__.py6
-rw-r--r--.venv/lib/python3.12/site-packages/ellipticcurve/curve.py90
-rw-r--r--.venv/lib/python3.12/site-packages/ellipticcurve/ecdsa.py46
-rw-r--r--.venv/lib/python3.12/site-packages/ellipticcurve/math.py181
-rw-r--r--.venv/lib/python3.12/site-packages/ellipticcurve/point.py14
-rw-r--r--.venv/lib/python3.12/site-packages/ellipticcurve/privateKey.py72
-rw-r--r--.venv/lib/python3.12/site-packages/ellipticcurve/publicKey.py107
-rw-r--r--.venv/lib/python3.12/site-packages/ellipticcurve/signature.py48
-rw-r--r--.venv/lib/python3.12/site-packages/ellipticcurve/utils/__init__.py0
-rw-r--r--.venv/lib/python3.12/site-packages/ellipticcurve/utils/binary.py37
-rw-r--r--.venv/lib/python3.12/site-packages/ellipticcurve/utils/compatibility.py40
-rw-r--r--.venv/lib/python3.12/site-packages/ellipticcurve/utils/der.py159
-rw-r--r--.venv/lib/python3.12/site-packages/ellipticcurve/utils/file.py9
-rw-r--r--.venv/lib/python3.12/site-packages/ellipticcurve/utils/integer.py16
-rw-r--r--.venv/lib/python3.12/site-packages/ellipticcurve/utils/oid.py35
-rw-r--r--.venv/lib/python3.12/site-packages/ellipticcurve/utils/pem.py14
16 files changed, 874 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/ellipticcurve/__init__.py b/.venv/lib/python3.12/site-packages/ellipticcurve/__init__.py
new file mode 100644
index 00000000..5f4727f4
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/ellipticcurve/__init__.py
@@ -0,0 +1,6 @@
+from .utils.compatibility import *
+from .privateKey import PrivateKey
+from .publicKey import PublicKey
+from .signature import Signature
+from .utils.file import File
+from .ecdsa import Ecdsa
diff --git a/.venv/lib/python3.12/site-packages/ellipticcurve/curve.py b/.venv/lib/python3.12/site-packages/ellipticcurve/curve.py
new file mode 100644
index 00000000..df3e119e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/ellipticcurve/curve.py
@@ -0,0 +1,90 @@
+#
+# Elliptic Curve Equation
+#
+# y^2 = x^3 + A*x + B (mod P)
+#
+from .math import Math
+from .point import Point
+
+
+class CurveFp:
+
+ def __init__(self, A, B, P, N, Gx, Gy, name, oid, nistName=None):
+ self.A = A
+ self.B = B
+ self.P = P
+ self.N = N
+ self.G = Point(Gx, Gy)
+ self.name = name
+ self.nistName = nistName
+ self.oid = oid # ASN.1 Object Identifier
+
+ def contains(self, p):
+ """
+ Verify if the point `p` is on the curve
+
+ :param p: Point p = Point(x, y)
+ :return: boolean
+ """
+ if not 0 <= p.x <= self.P - 1:
+ return False
+ if not 0 <= p.y <= self.P - 1:
+ return False
+ if (p.y**2 - (p.x**3 + self.A * p.x + self.B)) % self.P != 0:
+ return False
+ return True
+
+ def length(self):
+ return (1 + len("%x" % self.N)) // 2
+
+ def y(self, x, isEven):
+ ySquared = (pow(x, 3, self.P) + self.A * x + self.B) % self.P
+ y = Math.modularSquareRoot(ySquared, self.P)
+ if isEven != (y % 2 == 0):
+ y = self.P - y
+ return y
+
+
+_curvesByOid = {tuple(curve.oid): curve for curve in []}
+
+
+def add(curve):
+ _curvesByOid[tuple(curve.oid)] = curve
+
+
+def getByOid(oid):
+ if oid not in _curvesByOid:
+ raise Exception("Unknown curve with oid {oid}; The following are registered: {names}".format(
+ oid=".".join([str(number) for number in oid]),
+ names=", ".join([curve.name for curve in _curvesByOid.values()]),
+ ))
+ return _curvesByOid[oid]
+
+
+secp256k1 = CurveFp(
+ name="secp256k1",
+ A=0x0000000000000000000000000000000000000000000000000000000000000000,
+ B=0x0000000000000000000000000000000000000000000000000000000000000007,
+ P=0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f,
+ N=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141,
+ Gx=0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,
+ Gy=0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8,
+ oid=[1, 3, 132, 0, 10]
+)
+
+prime256v1 = CurveFp(
+ name="prime256v1",
+ nistName="P-256",
+ A=0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc,
+ B=0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b,
+ P=0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff,
+ N=0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551,
+ Gx=0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296,
+ Gy=0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5,
+ oid=[1, 2, 840, 10045, 3, 1, 7],
+)
+
+p256 = prime256v1
+
+add(secp256k1)
+add(prime256v1)
diff --git a/.venv/lib/python3.12/site-packages/ellipticcurve/ecdsa.py b/.venv/lib/python3.12/site-packages/ellipticcurve/ecdsa.py
new file mode 100644
index 00000000..ea809e4f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/ellipticcurve/ecdsa.py
@@ -0,0 +1,46 @@
+from hashlib import sha256
+from .signature import Signature
+from .math import Math
+from .utils.integer import RandomInteger
+from .utils.binary import numberFromByteString
+from .utils.compatibility import *
+
+
+class Ecdsa:
+
+ @classmethod
+ def sign(cls, message, privateKey, hashfunc=sha256):
+ byteMessage = hashfunc(toBytes(message)).digest()
+ numberMessage = numberFromByteString(byteMessage)
+ curve = privateKey.curve
+
+ r, s, randSignPoint = 0, 0, None
+ while r == 0 or s == 0:
+ randNum = RandomInteger.between(1, curve.N - 1)
+ randSignPoint = Math.multiply(curve.G, n=randNum, A=curve.A, P=curve.P, N=curve.N)
+ r = randSignPoint.x % curve.N
+ s = ((numberMessage + r * privateKey.secret) * (Math.inv(randNum, curve.N))) % curve.N
+ recoveryId = randSignPoint.y & 1
+ if randSignPoint.y > curve.N:
+ recoveryId += 2
+
+ return Signature(r=r, s=s, recoveryId=recoveryId)
+
+ @classmethod
+ def verify(cls, message, signature, publicKey, hashfunc=sha256):
+ byteMessage = hashfunc(toBytes(message)).digest()
+ numberMessage = numberFromByteString(byteMessage)
+ curve = publicKey.curve
+ r = signature.r
+ s = signature.s
+ if not 1 <= r <= curve.N - 1:
+ return False
+ if not 1 <= s <= curve.N - 1:
+ return False
+ inv = Math.inv(s, curve.N)
+ u1 = Math.multiply(curve.G, n=(numberMessage * inv) % curve.N, N=curve.N, A=curve.A, P=curve.P)
+ u2 = Math.multiply(publicKey.point, n=(r * inv) % curve.N, N=curve.N, A=curve.A, P=curve.P)
+ v = Math.add(u1, u2, A=curve.A, P=curve.P)
+ if v.isAtInfinity():
+ return False
+ return v.x % curve.N == r
diff --git a/.venv/lib/python3.12/site-packages/ellipticcurve/math.py b/.venv/lib/python3.12/site-packages/ellipticcurve/math.py
new file mode 100644
index 00000000..981ab4e7
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/ellipticcurve/math.py
@@ -0,0 +1,181 @@
+from .point import Point
+
+
+class Math:
+
+ @classmethod
+ def modularSquareRoot(cls, value, prime):
+ return pow(value, (prime + 1) // 4, prime)
+
+ @classmethod
+ def multiply(cls, p, n, N, A, P):
+ """
+ Fast way to multily point and scalar in elliptic curves
+
+ :param p: First Point to mutiply
+ :param n: Scalar to mutiply
+ :param N: Order of the elliptic curve
+ :param P: Prime number in the module of the equation Y^2 = X^3 + A*X + B (mod p)
+ :param A: Coefficient of the first-order term of the equation Y^2 = X^3 + A*X + B (mod p)
+ :return: Point that represents the sum of First and Second Point
+ """
+ return cls._fromJacobian(
+ cls._jacobianMultiply(cls._toJacobian(p), n, N, A, P), P
+ )
+
+ @classmethod
+ def add(cls, p, q, A, P):
+ """
+ Fast way to add two points in elliptic curves
+
+ :param p: First Point you want to add
+ :param q: Second Point you want to add
+ :param P: Prime number in the module of the equation Y^2 = X^3 + A*X + B (mod p)
+ :param A: Coefficient of the first-order term of the equation Y^2 = X^3 + A*X + B (mod p)
+ :return: Point that represents the sum of First and Second Point
+ """
+ return cls._fromJacobian(
+ cls._jacobianAdd(cls._toJacobian(p), cls._toJacobian(q), A, P), P,
+ )
+
+ @classmethod
+ def inv(cls, x, n):
+ """
+ Extended Euclidean Algorithm. It's the 'division' in elliptic curves
+
+ :param x: Divisor
+ :param n: Mod for division
+ :return: Value representing the division
+ """
+ if x == 0:
+ return 0
+
+ lm = 1
+ hm = 0
+ low = x % n
+ high = n
+
+ while low > 1:
+ r = high // low
+ nm = hm - lm * r
+ nw = high - low * r
+ high = low
+ hm = lm
+ low = nw
+ lm = nm
+
+ return lm % n
+
+ @classmethod
+ def _toJacobian(cls, p):
+ """
+ Convert point to Jacobian coordinates
+
+ :param p: First Point you want to add
+ :return: Point in Jacobian coordinates
+ """
+ return Point(p.x, p.y, 1)
+
+ @classmethod
+ def _fromJacobian(cls, p, P):
+ """
+ Convert point back from Jacobian coordinates
+
+ :param p: First Point you want to add
+ :param P: Prime number in the module of the equation Y^2 = X^3 + A*X + B (mod p)
+ :return: Point in default coordinates
+ """
+ z = cls.inv(p.z, P)
+ x = (p.x * z ** 2) % P
+ y = (p.y * z ** 3) % P
+
+ return Point(x, y, 0)
+
+ @classmethod
+ def _jacobianDouble(cls, p, A, P):
+ """
+ Double a point in elliptic curves
+
+ :param p: Point you want to double
+ :param P: Prime number in the module of the equation Y^2 = X^3 + A*X + B (mod p)
+ :param A: Coefficient of the first-order term of the equation Y^2 = X^3 + A*X + B (mod p)
+ :return: Point that represents the sum of First and Second Point
+ """
+ if p.y == 0:
+ return Point(0, 0, 0)
+
+ ysq = (p.y ** 2) % P
+ S = (4 * p.x * ysq) % P
+ M = (3 * p.x ** 2 + A * p.z ** 4) % P
+ nx = (M**2 - 2 * S) % P
+ ny = (M * (S - nx) - 8 * ysq ** 2) % P
+ nz = (2 * p.y * p.z) % P
+
+ return Point(nx, ny, nz)
+
+ @classmethod
+ def _jacobianAdd(cls, p, q, A, P):
+ """
+ Add two points in elliptic curves
+
+ :param p: First Point you want to add
+ :param q: Second Point you want to add
+ :param P: Prime number in the module of the equation Y^2 = X^3 + A*X + B (mod p)
+ :param A: Coefficient of the first-order term of the equation Y^2 = X^3 + A*X + B (mod p)
+ :return: Point that represents the sum of First and Second Point
+ """
+ if p.y == 0:
+ return q
+ if q.y == 0:
+ return p
+
+ U1 = (p.x * q.z ** 2) % P
+ U2 = (q.x * p.z ** 2) % P
+ S1 = (p.y * q.z ** 3) % P
+ S2 = (q.y * p.z ** 3) % P
+
+ if U1 == U2:
+ if S1 != S2:
+ return Point(0, 0, 1)
+ return cls._jacobianDouble(p, A, P)
+
+ H = U2 - U1
+ R = S2 - S1
+ H2 = (H * H) % P
+ H3 = (H * H2) % P
+ U1H2 = (U1 * H2) % P
+ nx = (R ** 2 - H3 - 2 * U1H2) % P
+ ny = (R * (U1H2 - nx) - S1 * H3) % P
+ nz = (H * p.z * q.z) % P
+
+ return Point(nx, ny, nz)
+
+ @classmethod
+ def _jacobianMultiply(cls, p, n, N, A, P):
+ """
+ Multily point and scalar in elliptic curves
+
+ :param p: First Point to mutiply
+ :param n: Scalar to mutiply
+ :param N: Order of the elliptic curve
+ :param P: Prime number in the module of the equation Y^2 = X^3 + A*X + B (mod p)
+ :param A: Coefficient of the first-order term of the equation Y^2 = X^3 + A*X + B (mod p)
+ :return: Point that represents the sum of First and Second Point
+ """
+ if p.y == 0 or n == 0:
+ return Point(0, 0, 1)
+
+ if n == 1:
+ return p
+
+ if n < 0 or n >= N:
+ return cls._jacobianMultiply(p, n % N, N, A, P)
+
+ if (n % 2) == 0:
+ return cls._jacobianDouble(
+ cls._jacobianMultiply(p, n // 2, N, A, P), A, P
+ )
+
+ return cls._jacobianAdd(
+ cls._jacobianDouble(cls._jacobianMultiply(p, n // 2, N, A, P), A, P), p, A, P
+ )
diff --git a/.venv/lib/python3.12/site-packages/ellipticcurve/point.py b/.venv/lib/python3.12/site-packages/ellipticcurve/point.py
new file mode 100644
index 00000000..b960a3a6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/ellipticcurve/point.py
@@ -0,0 +1,14 @@
+
+
+class Point:
+
+ def __init__(self, x=0, y=0, z=0):
+ self.x = x
+ self.y = y
+ self.z = z
+
+ def __str__(self):
+ return "({x}, {y}, {z})".format(x=self.x, y=self.y, z=self.z)
+
+ def isAtInfinity(self):
+ return self.y == 0
diff --git a/.venv/lib/python3.12/site-packages/ellipticcurve/privateKey.py b/.venv/lib/python3.12/site-packages/ellipticcurve/privateKey.py
new file mode 100644
index 00000000..df6fb4d9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/ellipticcurve/privateKey.py
@@ -0,0 +1,72 @@
+from .math import Math
+from .utils.integer import RandomInteger
+from .utils.pem import getPemContent, createPem
+from .utils.binary import hexFromByteString, byteStringFromHex, intFromHex, base64FromByteString, byteStringFromBase64
+from .utils.der import hexFromInt, parse, encodeConstructed, DerFieldType, encodePrimitive
+from .curve import secp256k1, getByOid
+from .publicKey import PublicKey
+
+
+class PrivateKey:
+
+ def __init__(self, curve=secp256k1, secret=None):
+ self.curve = curve
+ self.secret = secret or RandomInteger.between(1, curve.N - 1)
+
+ def publicKey(self):
+ curve = self.curve
+ publicPoint = Math.multiply(
+ p=curve.G,
+ n=self.secret,
+ N=curve.N,
+ A=curve.A,
+ P=curve.P,
+ )
+ return PublicKey(point=publicPoint, curve=curve)
+
+ def toString(self):
+ return hexFromInt(self.secret)
+
+ def toDer(self):
+ publicKeyString = self.publicKey().toString(encoded=True)
+ hexadecimal = encodeConstructed(
+ encodePrimitive(DerFieldType.integer, 1),
+ encodePrimitive(DerFieldType.octetString, hexFromInt(self.secret)),
+ encodePrimitive(DerFieldType.oidContainer, encodePrimitive(DerFieldType.object, self.curve.oid)),
+ encodePrimitive(DerFieldType.publicKeyPointContainer, encodePrimitive(DerFieldType.bitString, publicKeyString))
+ )
+ return byteStringFromHex(hexadecimal)
+
+ def toPem(self):
+ der = self.toDer()
+ return createPem(content=base64FromByteString(der), template=_pemTemplate)
+
+ @classmethod
+ def fromPem(cls, string):
+ privateKeyPem = getPemContent(pem=string, template=_pemTemplate)
+ return cls.fromDer(byteStringFromBase64(privateKeyPem))
+
+ @classmethod
+ def fromDer(cls, string):
+ hexadecimal = hexFromByteString(string)
+ privateKeyFlag, secretHex, curveData, publicKeyString = parse(hexadecimal)[0]
+ if privateKeyFlag != 1:
+ raise Exception("Private keys should start with a '1' flag, but a '{flag}' was found instead".format(
+ flag=privateKeyFlag
+ ))
+ curve = getByOid(curveData[0])
+ privateKey = cls.fromString(string=secretHex, curve=curve)
+ if privateKey.publicKey().toString(encoded=True) != publicKeyString[0]:
+ raise Exception("The public key described inside the private key file doesn't match the actual public key of the pair")
+ return privateKey
+
+ @classmethod
+ def fromString(cls, string, curve=secp256k1):
+ return PrivateKey(secret=intFromHex(string), curve=curve)
+
+
+_pemTemplate = """
+-----BEGIN EC PRIVATE KEY-----
+{content}
+-----END EC PRIVATE KEY-----
+"""
diff --git a/.venv/lib/python3.12/site-packages/ellipticcurve/publicKey.py b/.venv/lib/python3.12/site-packages/ellipticcurve/publicKey.py
new file mode 100644
index 00000000..3ebb5938
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/ellipticcurve/publicKey.py
@@ -0,0 +1,107 @@
+from .math import Math
+from .point import Point
+from .curve import secp256k1, getByOid
+from .utils.pem import getPemContent, createPem
+from .utils.der import hexFromInt, parse, DerFieldType, encodeConstructed, encodePrimitive
+from .utils.binary import hexFromByteString, byteStringFromHex, intFromHex, base64FromByteString, byteStringFromBase64
+
+
+class PublicKey:
+
+ def __init__(self, point, curve):
+ self.point = point
+ self.curve = curve
+
+ def toString(self, encoded=False):
+ baseLength = 2 * self.curve.length()
+ xHex = hexFromInt(self.point.x).zfill(baseLength)
+ yHex = hexFromInt(self.point.y).zfill(baseLength)
+ string = xHex + yHex
+ if encoded:
+ return "0004" + string
+ return string
+
+ def toCompressed(self):
+ baseLength = 2 * self.curve.length()
+ parityTag = _evenTag if self.point.y % 2 == 0 else _oddTag
+ xHex = hexFromInt(self.point.x).zfill(baseLength)
+ return parityTag + xHex
+
+ def toDer(self):
+ hexadecimal = encodeConstructed(
+ encodeConstructed(
+ encodePrimitive(DerFieldType.object, _ecdsaPublicKeyOid),
+ encodePrimitive(DerFieldType.object, self.curve.oid),
+ ),
+ encodePrimitive(DerFieldType.bitString, self.toString(encoded=True)),
+ )
+ return byteStringFromHex(hexadecimal)
+
+ def toPem(self):
+ der = self.toDer()
+ return createPem(content=base64FromByteString(der), template=_pemTemplate)
+
+ @classmethod
+ def fromPem(cls, string):
+ publicKeyPem = getPemContent(pem=string, template=_pemTemplate)
+ return cls.fromDer(byteStringFromBase64(publicKeyPem))
+
+ @classmethod
+ def fromDer(cls, string):
+ hexadecimal = hexFromByteString(string)
+ curveData, pointString = parse(hexadecimal)[0]
+ publicKeyOid, curveOid = curveData
+ if publicKeyOid != _ecdsaPublicKeyOid:
+ raise Exception("The Public Key Object Identifier (OID) should be {ecdsaPublicKeyOid}, but {actualOid} was found instead".format(
+ ecdsaPublicKeyOid=_ecdsaPublicKeyOid,
+ actualOid=publicKeyOid,
+ ))
+ curve = getByOid(curveOid)
+ return cls.fromString(string=pointString, curve=curve)
+
+ @classmethod
+ def fromString(cls, string, curve=secp256k1, validatePoint=True):
+ baseLength = 2 * curve.length()
+ if len(string) > 2 * baseLength and string[:4] == "0004":
+ string = string[4:]
+
+ xs = string[:baseLength]
+ ys = string[baseLength:]
+
+ p = Point(
+ x=intFromHex(xs),
+ y=intFromHex(ys),
+ )
+ publicKey = PublicKey(point=p, curve=curve)
+ if not validatePoint:
+ return publicKey
+ if p.isAtInfinity():
+ raise Exception("Public Key point is at infinity")
+ if not curve.contains(p):
+ raise Exception("Point ({x},{y}) is not valid for curve {name}".format(x=p.x, y=p.y, name=curve.name))
+ if not Math.multiply(p=p, n=curve.N, N=curve.N, A=curve.A, P=curve.P).isAtInfinity():
+ raise Exception("Point ({x},{y}) * {name}.N is not at infinity".format(x=p.x, y=p.y, name=curve.name))
+ return publicKey
+
+ @classmethod
+ def fromCompressed(cls, string, curve=secp256k1):
+ parityTag, xHex = string[:2], string[2:]
+ if parityTag not in [_evenTag, _oddTag]:
+ raise Exception("Compressed string should start with 02 or 03")
+ x = intFromHex(xHex)
+ y = curve.y(x, isEven=parityTag == _evenTag)
+ return cls(point=Point(x, y), curve=curve)
+
+
+_evenTag = "02"
+_oddTag = "03"
+
+
+_ecdsaPublicKeyOid = (1, 2, 840, 10045, 2, 1)
+
+
+_pemTemplate = """
+-----BEGIN PUBLIC KEY-----
+{content}
+-----END PUBLIC KEY-----
+"""
diff --git a/.venv/lib/python3.12/site-packages/ellipticcurve/signature.py b/.venv/lib/python3.12/site-packages/ellipticcurve/signature.py
new file mode 100644
index 00000000..3084f8f8
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/ellipticcurve/signature.py
@@ -0,0 +1,48 @@
+from .utils.compatibility import *
+from .utils.der import parse, encodeConstructed, encodePrimitive, DerFieldType
+from .utils.binary import hexFromByteString, byteStringFromHex, base64FromByteString, byteStringFromBase64
+
+
+class Signature:
+
+ def __init__(self, r, s, recoveryId=None):
+ self.r = r
+ self.s = s
+ self.recoveryId = recoveryId
+
+ def toDer(self, withRecoveryId=False):
+ hexadecimal = self._toString()
+ encodedSequence = byteStringFromHex(hexadecimal)
+ if not withRecoveryId:
+ return encodedSequence
+ return toBytes(chr(27 + self.recoveryId)) + encodedSequence
+
+ def toBase64(self, withRecoveryId=False):
+ return base64FromByteString(self.toDer(withRecoveryId))
+
+ @classmethod
+ def fromDer(cls, string, recoveryByte=False):
+ recoveryId = None
+ if recoveryByte:
+ recoveryId = string[0] if isinstance(string[0], intTypes) else ord(string[0])
+ recoveryId -= 27
+ string = string[1:]
+
+ hexadecimal = hexFromByteString(string)
+ return cls._fromString(string=hexadecimal, recoveryId=recoveryId)
+
+ @classmethod
+ def fromBase64(cls, string, recoveryByte=False):
+ der = byteStringFromBase64(string)
+ return cls.fromDer(der, recoveryByte)
+
+ def _toString(self):
+ return encodeConstructed(
+ encodePrimitive(DerFieldType.integer, self.r),
+ encodePrimitive(DerFieldType.integer, self.s),
+ )
+
+ @classmethod
+ def _fromString(cls, string, recoveryId=None):
+ r, s = parse(string)[0]
+ return Signature(r=r, s=s, recoveryId=recoveryId)
diff --git a/.venv/lib/python3.12/site-packages/ellipticcurve/utils/__init__.py b/.venv/lib/python3.12/site-packages/ellipticcurve/utils/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/ellipticcurve/utils/__init__.py
diff --git a/.venv/lib/python3.12/site-packages/ellipticcurve/utils/binary.py b/.venv/lib/python3.12/site-packages/ellipticcurve/utils/binary.py
new file mode 100644
index 00000000..348887f0
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/ellipticcurve/utils/binary.py
@@ -0,0 +1,37 @@
+from base64 import b64encode, b64decode
+from .compatibility import safeHexFromBinary, safeBinaryFromHex, toString
+
+
+def hexFromInt(number):
+ hexadecimal = "{0:x}".format(number)
+ if len(hexadecimal) % 2 == 1:
+ hexadecimal = "0" + hexadecimal
+ return hexadecimal
+
+
+def intFromHex(hexadecimal):
+ return int(hexadecimal, 16)
+
+
+def hexFromByteString(byteString):
+ return safeHexFromBinary(byteString)
+
+
+def byteStringFromHex(hexadecimal):
+ return safeBinaryFromHex(hexadecimal)
+
+
+def numberFromByteString(byteString):
+ return intFromHex(hexFromByteString(byteString))
+
+
+def base64FromByteString(byteString):
+ return toString(b64encode(byteString))
+
+
+def byteStringFromBase64(base64String):
+ return b64decode(base64String)
+
+
+def bitsFromHex(hexadecimal):
+ return format(intFromHex(hexadecimal), 'b').zfill(4 * len(hexadecimal))
diff --git a/.venv/lib/python3.12/site-packages/ellipticcurve/utils/compatibility.py b/.venv/lib/python3.12/site-packages/ellipticcurve/utils/compatibility.py
new file mode 100644
index 00000000..3b22dd3c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/ellipticcurve/utils/compatibility.py
@@ -0,0 +1,40 @@
+from sys import version_info as pyVersion
+from binascii import hexlify, unhexlify
+
+
+if pyVersion.major == 3:
+ # py3 constants and conversion functions
+
+ stringTypes = (str,)
+ intTypes = (int, float)
+
+ def toString(string, encoding="utf-8"):
+ return string.decode(encoding)
+
+ def toBytes(string, encoding="utf-8"):
+ return string.encode(encoding)
+
+ def safeBinaryFromHex(hexadecimal):
+ if len(hexadecimal) % 2 == 1:
+ hexadecimal = "0" + hexadecimal
+ return unhexlify(hexadecimal)
+
+ def safeHexFromBinary(byteString):
+ return toString(hexlify(byteString))
+else:
+ # py2 constants and conversion functions
+
+ stringTypes = (str, unicode)
+ intTypes = (int, float, long)
+
+ def toString(string, encoding="utf-8"):
+ return string
+
+ def toBytes(string, encoding="utf-8"):
+ return string
+
+ def safeBinaryFromHex(hexadecimal):
+ return unhexlify(hexadecimal)
+
+ def safeHexFromBinary(byteString):
+ return hexlify(byteString)
diff --git a/.venv/lib/python3.12/site-packages/ellipticcurve/utils/der.py b/.venv/lib/python3.12/site-packages/ellipticcurve/utils/der.py
new file mode 100644
index 00000000..84546aea
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/ellipticcurve/utils/der.py
@@ -0,0 +1,159 @@
+from datetime import datetime
+from .oid import oidToHex, oidFromHex
+from .binary import hexFromInt, intFromHex, byteStringFromHex, bitsFromHex
+
+
+class DerFieldType:
+
+ integer = "integer"
+ bitString = "bitString"
+ octetString = "octetString"
+ null = "null"
+ object = "object"
+ printableString = "printableString"
+ utcTime = "utcTime"
+ sequence = "sequence"
+ set = "set"
+ oidContainer = "oidContainer"
+ publicKeyPointContainer = "publicKeyPointContainer"
+
+
+_hexTagToType = {
+ "02": DerFieldType.integer,
+ "03": DerFieldType.bitString,
+ "04": DerFieldType.octetString,
+ "05": DerFieldType.null,
+ "06": DerFieldType.object,
+ "13": DerFieldType.printableString,
+ "17": DerFieldType.utcTime,
+ "30": DerFieldType.sequence,
+ "31": DerFieldType.set,
+ "a0": DerFieldType.oidContainer,
+ "a1": DerFieldType.publicKeyPointContainer,
+}
+_typeToHexTag = {v: k for k, v in _hexTagToType.items()}
+
+
+def encodeConstructed(*encodedValues):
+ return encodePrimitive(DerFieldType.sequence, "".join(encodedValues))
+
+
+def encodePrimitive(tagType, value):
+ if tagType == DerFieldType.integer:
+ value = _encodeInteger(value)
+ if tagType == DerFieldType.object:
+ value = oidToHex(value)
+ return "{tag}{size}{value}".format(tag=_typeToHexTag[tagType], size=_generateLengthBytes(value), value=value)
+
+
+def parse(hexadecimal):
+ if not hexadecimal:
+ return []
+ typeByte, hexadecimal = hexadecimal[:2], hexadecimal[2:]
+ length, lengthBytes = _readLengthBytes(hexadecimal)
+ content, hexadecimal = hexadecimal[lengthBytes: lengthBytes + length], hexadecimal[lengthBytes + length:]
+ if len(content) < length:
+ raise Exception("missing bytes in DER parse")
+
+ tagData = _getTagData(typeByte)
+ if tagData["isConstructed"]:
+ content = parse(content)
+
+ valueParser = {
+ DerFieldType.null: _parseNull,
+ DerFieldType.object: _parseOid,
+ DerFieldType.utcTime: _parseTime,
+ DerFieldType.integer: _parseInteger,
+ DerFieldType.printableString: _parseString,
+ }.get(tagData["type"], _parseAny)
+ return [valueParser(content)] + parse(hexadecimal)
+
+
+def _parseAny(hexadecimal):
+ return hexadecimal
+
+
+def _parseOid(hexadecimal):
+ return tuple(oidFromHex(hexadecimal))
+
+
+def _parseTime(hexadecimal):
+ string = _parseString(hexadecimal)
+ return datetime.strptime(string, "%y%m%d%H%M%SZ")
+
+
+def _parseString(hexadecimal):
+ return byteStringFromHex(hexadecimal).decode()
+
+
+def _parseNull(_content):
+ return None
+
+
+def _parseInteger(hexadecimal):
+ integer = intFromHex(hexadecimal)
+ bits = bitsFromHex(hexadecimal[0])
+ if bits[0] == "0": # negative numbers are encoded using two's complement
+ return integer
+ bitCount = 4 * len(hexadecimal)
+ return integer - (2 ** bitCount)
+
+
+def _encodeInteger(number):
+ hexadecimal = hexFromInt(abs(number))
+ if number < 0:
+ bitCount = 4 * len(hexadecimal)
+ twosComplement = (2 ** bitCount) + number
+ return hexFromInt(twosComplement)
+ bits = bitsFromHex(hexadecimal[0])
+ if bits[0] == "1": # if first bit was left as 1, number would be parsed as a negative integer with two's complement
+ hexadecimal = "00" + hexadecimal
+ return hexadecimal
+
+
+def _readLengthBytes(hexadecimal):
+ lengthBytes = 2
+ lengthIndicator = intFromHex(hexadecimal[0:lengthBytes])
+ isShortForm = lengthIndicator < 128 # checks if first bit of byte is 1 (a.k.a. short-form)
+ if isShortForm:
+ length = lengthIndicator * 2
+ return length, lengthBytes
+
+ lengthLength = lengthIndicator - 128 # nullifies first bit of byte (only used as long-form flag)
+ if lengthLength == 0:
+ raise Exception("indefinite length encoding located in DER")
+ lengthBytes += 2 * lengthLength
+ length = intFromHex(hexadecimal[2:lengthBytes]) * 2
+ return length, lengthBytes
+
+
+def _generateLengthBytes(hexadecimal):
+ size = len(hexadecimal) // 2
+ length = hexFromInt(size)
+ if size < 128: # checks if first bit of byte should be 0 (a.k.a. short-form flag)
+ return length.zfill(2)
+ lengthLength = 128 + len(length) // 2 # +128 sets the first bit of the byte as 1 (a.k.a. long-form flag)
+ return hexFromInt(lengthLength) + length
+
+
+def _getTagData(tag):
+ bits = bitsFromHex(tag)
+ bit8, bit7, bit6 = bits[:3]
+
+ tagClass = {
+ "0": {
+ "0": "universal",
+ "1": "application",
+ },
+ "1": {
+ "0": "context-specific",
+ "1": "private",
+ },
+ }[bit8][bit7]
+ isConstructed = bit6 == "1"
+
+ return {
+ "class": tagClass,
+ "isConstructed": isConstructed,
+ "type": _hexTagToType.get(tag),
+ }
diff --git a/.venv/lib/python3.12/site-packages/ellipticcurve/utils/file.py b/.venv/lib/python3.12/site-packages/ellipticcurve/utils/file.py
new file mode 100644
index 00000000..c7b1df73
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/ellipticcurve/utils/file.py
@@ -0,0 +1,9 @@
+
+
+class File:
+
+ @classmethod
+ def read(cls, path, mode="r"):
+ with open(path, mode) as blob:
+ content = blob.read()
+ return content
diff --git a/.venv/lib/python3.12/site-packages/ellipticcurve/utils/integer.py b/.venv/lib/python3.12/site-packages/ellipticcurve/utils/integer.py
new file mode 100644
index 00000000..180f200c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/ellipticcurve/utils/integer.py
@@ -0,0 +1,16 @@
+from random import SystemRandom
+
+
+class RandomInteger:
+
+ @classmethod
+ def between(cls, min, max):
+ """
+ Return integer x in the range: min <= x <= max
+
+ :param min: minimum value of the integer
+ :param max: maximum value of the integer
+ :return:
+ """
+
+ return SystemRandom().randrange(min, max + 1)
diff --git a/.venv/lib/python3.12/site-packages/ellipticcurve/utils/oid.py b/.venv/lib/python3.12/site-packages/ellipticcurve/utils/oid.py
new file mode 100644
index 00000000..dbfebe79
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/ellipticcurve/utils/oid.py
@@ -0,0 +1,35 @@
+from .binary import intFromHex, hexFromInt
+
+
+def oidFromHex(hexadecimal):
+ firstByte, remainingBytes = hexadecimal[:2], hexadecimal[2:]
+ firstByteInt = intFromHex(firstByte)
+ oid = [firstByteInt // 40, firstByteInt % 40]
+ oidInt = 0
+ while len(remainingBytes) > 0:
+ byte, remainingBytes = remainingBytes[0:2], remainingBytes[2:]
+ byteInt = intFromHex(byte)
+ if byteInt >= 128:
+ oidInt = (128 * oidInt) + (byteInt - 128)
+ continue
+ oidInt = (128 * oidInt) + byteInt
+ oid.append(oidInt)
+ oidInt = 0
+ return oid
+
+
+def oidToHex(oid):
+ hexadecimal = hexFromInt(40 * oid[0] + oid[1])
+ for number in oid[2:]:
+ hexadecimal += _oidNumberToHex(number)
+ return hexadecimal
+
+
+def _oidNumberToHex(number):
+ hexadecimal = ""
+ endDelta = 0
+ while number > 0:
+ hexadecimal = hexFromInt((number % 128) + endDelta) + hexadecimal
+ number //= 128
+ endDelta = 128
+ return hexadecimal or "00"
diff --git a/.venv/lib/python3.12/site-packages/ellipticcurve/utils/pem.py b/.venv/lib/python3.12/site-packages/ellipticcurve/utils/pem.py
new file mode 100644
index 00000000..1e58b409
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/ellipticcurve/utils/pem.py
@@ -0,0 +1,14 @@
+from re import search
+
+
+def getPemContent(pem, template):
+ pattern = template.format(content="(.*)")
+ return search("".join(pattern.splitlines()), "".join(pem.splitlines())).group(1)
+
+
+def createPem(content, template):
+ lines = [
+ content[start:start + 64]
+ for start in range(0, len(content), 64)
+ ]
+ return template.format(content="\n".join(lines))