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
« 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
6class DerFieldType:
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"
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()}
37def encodeConstructed(*encodedValues):
38 return encodePrimitive(DerFieldType.sequence, "".join(encodedValues))
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)
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")
58 tagData = _getTagData(typeByte)
59 if tagData["isConstructed"]:
60 content = parse(content)
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)
72def _parseAny(hexadecimal):
73 return hexadecimal
76def _parseOid(hexadecimal):
77 return tuple(oidFromHex(hexadecimal))
80def _parseTime(hexadecimal):
81 string = _parseString(hexadecimal)
82 return datetime.strptime(string, "%y%m%d%H%M%SZ")
85def _parseString(hexadecimal):
86 return byteStringFromHex(hexadecimal).decode()
89def _parseNull(_content):
90 return None
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)
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
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
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
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
139def _getTagData(tag):
140 bits = bitsFromHex(tag)
141 bit8, bit7, bit6 = bits[:3]
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"
155 return {
156 "class": tagClass,
157 "isConstructed": isConstructed,
158 "type": _hexTagToType.get(tag),
159 }