Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/ellipticcurve/publicKey.py: 29%
72 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1from .math import Math
2from .point import Point
3from .curve import secp256k1, getByOid
4from .utils.pem import getPemContent, createPem
5from .utils.der import hexFromInt, parse, DerFieldType, encodeConstructed, encodePrimitive
6from .utils.binary import hexFromByteString, byteStringFromHex, intFromHex, base64FromByteString, byteStringFromBase64
9class PublicKey:
11 def __init__(self, point, curve):
12 self.point = point
13 self.curve = curve
15 def toString(self, encoded=False):
16 baseLength = 2 * self.curve.length()
17 xHex = hexFromInt(self.point.x).zfill(baseLength)
18 yHex = hexFromInt(self.point.y).zfill(baseLength)
19 string = xHex + yHex
20 if encoded:
21 return "0004" + string
22 return string
24 def toCompressed(self):
25 baseLength = 2 * self.curve.length()
26 parityTag = _evenTag if self.point.y % 2 == 0 else _oddTag
27 xHex = hexFromInt(self.point.x).zfill(baseLength)
28 return parityTag + xHex
30 def toDer(self):
31 hexadecimal = encodeConstructed(
32 encodeConstructed(
33 encodePrimitive(DerFieldType.object, _ecdsaPublicKeyOid),
34 encodePrimitive(DerFieldType.object, self.curve.oid),
35 ),
36 encodePrimitive(DerFieldType.bitString, self.toString(encoded=True)),
37 )
38 return byteStringFromHex(hexadecimal)
40 def toPem(self):
41 der = self.toDer()
42 return createPem(content=base64FromByteString(der), template=_pemTemplate)
44 @classmethod
45 def fromPem(cls, string):
46 publicKeyPem = getPemContent(pem=string, template=_pemTemplate)
47 return cls.fromDer(byteStringFromBase64(publicKeyPem))
49 @classmethod
50 def fromDer(cls, string):
51 hexadecimal = hexFromByteString(string)
52 curveData, pointString = parse(hexadecimal)[0]
53 publicKeyOid, curveOid = curveData
54 if publicKeyOid != _ecdsaPublicKeyOid:
55 raise Exception("The Public Key Object Identifier (OID) should be {ecdsaPublicKeyOid}, but {actualOid} was found instead".format(
56 ecdsaPublicKeyOid=_ecdsaPublicKeyOid,
57 actualOid=publicKeyOid,
58 ))
59 curve = getByOid(curveOid)
60 return cls.fromString(string=pointString, curve=curve)
62 @classmethod
63 def fromString(cls, string, curve=secp256k1, validatePoint=True):
64 baseLength = 2 * curve.length()
65 if len(string) > 2 * baseLength and string[:4] == "0004":
66 string = string[4:]
68 xs = string[:baseLength]
69 ys = string[baseLength:]
71 p = Point(
72 x=intFromHex(xs),
73 y=intFromHex(ys),
74 )
75 publicKey = PublicKey(point=p, curve=curve)
76 if not validatePoint:
77 return publicKey
78 if p.isAtInfinity():
79 raise Exception("Public Key point is at infinity")
80 if not curve.contains(p):
81 raise Exception("Point ({x},{y}) is not valid for curve {name}".format(x=p.x, y=p.y, name=curve.name))
82 if not Math.multiply(p=p, n=curve.N, N=curve.N, A=curve.A, P=curve.P).isAtInfinity():
83 raise Exception("Point ({x},{y}) * {name}.N is not at infinity".format(x=p.x, y=p.y, name=curve.name))
84 return publicKey
86 @classmethod
87 def fromCompressed(cls, string, curve=secp256k1):
88 parityTag, xHex = string[:2], string[2:]
89 if parityTag not in [_evenTag, _oddTag]:
90 raise Exception("Compressed string should start with 02 or 03")
91 x = intFromHex(xHex)
92 y = curve.y(x, isEven=parityTag == _evenTag)
93 return cls(point=Point(x, y), curve=curve)
96_evenTag = "02"
97_oddTag = "03"
100_ecdsaPublicKeyOid = (1, 2, 840, 10045, 2, 1)
103_pemTemplate = """
104-----BEGIN PUBLIC KEY-----
105{content}
106-----END PUBLIC KEY-----
107"""