1from .utils.compatibility import *
2from .utils.der import parse, encodeConstructed, encodePrimitive, DerFieldType
3from .utils.binary import hexFromByteString, byteStringFromHex, base64FromByteString, byteStringFromBase64
4
5
6class Signature:
7
8 def __init__(self, r, s, recoveryId=None):
9 self.r = r
10 self.s = s
11 self.recoveryId = recoveryId
12
13 def toDer(self, withRecoveryId=False):
14 hexadecimal = self._toString()
15 encodedSequence = byteStringFromHex(hexadecimal)
16 if not withRecoveryId:
17 return encodedSequence
18 return toBytes(chr(27 + self.recoveryId)) + encodedSequence
19
20 def toBase64(self, withRecoveryId=False):
21 return base64FromByteString(self.toDer(withRecoveryId))
22
23 @classmethod
24 def fromDer(cls, string, recoveryByte=False):
25 recoveryId = None
26 if recoveryByte:
27 rawByte = string[0] if isinstance(string[0], intTypes) else ord(string[0])
28 if not 27 <= rawByte <= 30:
29 raise Exception("Recovery byte must be in [27, 30], got {b}".format(b=rawByte))
30 recoveryId = rawByte - 27
31 string = string[1:]
32
33 hexadecimal = hexFromByteString(string)
34 signature = cls._fromString(string=hexadecimal, recoveryId=recoveryId)
35 if byteStringFromHex(signature._toString()) != string:
36 raise Exception("Signature is not in canonical DER form")
37 return signature
38
39 @classmethod
40 def fromBase64(cls, string, recoveryByte=False):
41 der = byteStringFromBase64(string)
42 return cls.fromDer(der, recoveryByte)
43
44 def _toString(self):
45 return encodeConstructed(
46 encodePrimitive(DerFieldType.integer, self.r),
47 encodePrimitive(DerFieldType.integer, self.s),
48 )
49
50 @classmethod
51 def _fromString(cls, string, recoveryId=None):
52 r, s = parse(string)[0]
53 return Signature(r=r, s=s, recoveryId=recoveryId)