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

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 

7 

8 

9class PublicKey: 

10 

11 def __init__(self, point, curve): 

12 self.point = point 

13 self.curve = curve 

14 

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 

23 

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 

29 

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) 

39 

40 def toPem(self): 

41 der = self.toDer() 

42 return createPem(content=base64FromByteString(der), template=_pemTemplate) 

43 

44 @classmethod 

45 def fromPem(cls, string): 

46 publicKeyPem = getPemContent(pem=string, template=_pemTemplate) 

47 return cls.fromDer(byteStringFromBase64(publicKeyPem)) 

48 

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) 

61 

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:] 

67 

68 xs = string[:baseLength] 

69 ys = string[baseLength:] 

70 

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 

85 

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) 

94 

95 

96_evenTag = "02" 

97_oddTag = "03" 

98 

99 

100_ecdsaPublicKeyOid = (1, 2, 840, 10045, 2, 1) 

101 

102 

103_pemTemplate = """ 

104-----BEGIN PUBLIC KEY----- 

105{content} 

106-----END PUBLIC KEY----- 

107"""