diff options
Diffstat (limited to '.venv/lib/python3.12/site-packages/ellipticcurve')
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)) |