Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/ellipticcurve/utils/der.py: 29%

92 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1from datetime import datetime 

2from .oid import oidToHex, oidFromHex 

3from .binary import hexFromInt, intFromHex, byteStringFromHex, bitsFromHex 

4 

5 

6class DerFieldType: 

7 

8 integer = "integer" 

9 bitString = "bitString" 

10 octetString = "octetString" 

11 null = "null" 

12 object = "object" 

13 printableString = "printableString" 

14 utcTime = "utcTime" 

15 sequence = "sequence" 

16 set = "set" 

17 oidContainer = "oidContainer" 

18 publicKeyPointContainer = "publicKeyPointContainer" 

19 

20 

21_hexTagToType = { 

22 "02": DerFieldType.integer, 

23 "03": DerFieldType.bitString, 

24 "04": DerFieldType.octetString, 

25 "05": DerFieldType.null, 

26 "06": DerFieldType.object, 

27 "13": DerFieldType.printableString, 

28 "17": DerFieldType.utcTime, 

29 "30": DerFieldType.sequence, 

30 "31": DerFieldType.set, 

31 "a0": DerFieldType.oidContainer, 

32 "a1": DerFieldType.publicKeyPointContainer, 

33} 

34_typeToHexTag = {v: k for k, v in _hexTagToType.items()} 

35 

36 

37def encodeConstructed(*encodedValues): 

38 return encodePrimitive(DerFieldType.sequence, "".join(encodedValues)) 

39 

40 

41def encodePrimitive(tagType, value): 

42 if tagType == DerFieldType.integer: 

43 value = _encodeInteger(value) 

44 if tagType == DerFieldType.object: 

45 value = oidToHex(value) 

46 return "{tag}{size}{value}".format(tag=_typeToHexTag[tagType], size=_generateLengthBytes(value), value=value) 

47 

48 

49def parse(hexadecimal): 

50 if not hexadecimal: 

51 return [] 

52 typeByte, hexadecimal = hexadecimal[:2], hexadecimal[2:] 

53 length, lengthBytes = _readLengthBytes(hexadecimal) 

54 content, hexadecimal = hexadecimal[lengthBytes: lengthBytes + length], hexadecimal[lengthBytes + length:] 

55 if len(content) < length: 

56 raise Exception("missing bytes in DER parse") 

57 

58 tagData = _getTagData(typeByte) 

59 if tagData["isConstructed"]: 

60 content = parse(content) 

61 

62 valueParser = { 

63 DerFieldType.null: _parseNull, 

64 DerFieldType.object: _parseOid, 

65 DerFieldType.utcTime: _parseTime, 

66 DerFieldType.integer: _parseInteger, 

67 DerFieldType.printableString: _parseString, 

68 }.get(tagData["type"], _parseAny) 

69 return [valueParser(content)] + parse(hexadecimal) 

70 

71 

72def _parseAny(hexadecimal): 

73 return hexadecimal 

74 

75 

76def _parseOid(hexadecimal): 

77 return tuple(oidFromHex(hexadecimal)) 

78 

79 

80def _parseTime(hexadecimal): 

81 string = _parseString(hexadecimal) 

82 return datetime.strptime(string, "%y%m%d%H%M%SZ") 

83 

84 

85def _parseString(hexadecimal): 

86 return byteStringFromHex(hexadecimal).decode() 

87 

88 

89def _parseNull(_content): 

90 return None 

91 

92 

93def _parseInteger(hexadecimal): 

94 integer = intFromHex(hexadecimal) 

95 bits = bitsFromHex(hexadecimal[0]) 

96 if bits[0] == "0": # negative numbers are encoded using two's complement 

97 return integer 

98 bitCount = 4 * len(hexadecimal) 

99 return integer - (2 ** bitCount) 

100 

101 

102def _encodeInteger(number): 

103 hexadecimal = hexFromInt(abs(number)) 

104 if number < 0: 

105 bitCount = 4 * len(hexadecimal) 

106 twosComplement = (2 ** bitCount) + number 

107 return hexFromInt(twosComplement) 

108 bits = bitsFromHex(hexadecimal[0]) 

109 if bits[0] == "1": # if first bit was left as 1, number would be parsed as a negative integer with two's complement 

110 hexadecimal = "00" + hexadecimal 

111 return hexadecimal 

112 

113 

114def _readLengthBytes(hexadecimal): 

115 lengthBytes = 2 

116 lengthIndicator = intFromHex(hexadecimal[0:lengthBytes]) 

117 isShortForm = lengthIndicator < 128 # checks if first bit of byte is 1 (a.k.a. short-form) 

118 if isShortForm: 

119 length = lengthIndicator * 2 

120 return length, lengthBytes 

121 

122 lengthLength = lengthIndicator - 128 # nullifies first bit of byte (only used as long-form flag) 

123 if lengthLength == 0: 

124 raise Exception("indefinite length encoding located in DER") 

125 lengthBytes += 2 * lengthLength 

126 length = intFromHex(hexadecimal[2:lengthBytes]) * 2 

127 return length, lengthBytes 

128 

129 

130def _generateLengthBytes(hexadecimal): 

131 size = len(hexadecimal) // 2 

132 length = hexFromInt(size) 

133 if size < 128: # checks if first bit of byte should be 0 (a.k.a. short-form flag) 

134 return length.zfill(2) 

135 lengthLength = 128 + len(length) // 2 # +128 sets the first bit of the byte as 1 (a.k.a. long-form flag) 

136 return hexFromInt(lengthLength) + length 

137 

138 

139def _getTagData(tag): 

140 bits = bitsFromHex(tag) 

141 bit8, bit7, bit6 = bits[:3] 

142 

143 tagClass = { 

144 "0": { 

145 "0": "universal", 

146 "1": "application", 

147 }, 

148 "1": { 

149 "0": "context-specific", 

150 "1": "private", 

151 }, 

152 }[bit8][bit7] 

153 isConstructed = bit6 == "1" 

154 

155 return { 

156 "class": tagClass, 

157 "isConstructed": isConstructed, 

158 "type": _hexTagToType.get(tag), 

159 }