aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/ellipticcurve/utils/der.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/ellipticcurve/utils/der.py')
-rw-r--r--.venv/lib/python3.12/site-packages/ellipticcurve/utils/der.py159
1 files changed, 159 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/ellipticcurve/utils/der.py b/.venv/lib/python3.12/site-packages/ellipticcurve/utils/der.py
new file mode 100644
index 00000000..84546aea
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/ellipticcurve/utils/der.py
@@ -0,0 +1,159 @@
+from datetime import datetime
+from .oid import oidToHex, oidFromHex
+from .binary import hexFromInt, intFromHex, byteStringFromHex, bitsFromHex
+
+
+class DerFieldType:
+
+ integer = "integer"
+ bitString = "bitString"
+ octetString = "octetString"
+ null = "null"
+ object = "object"
+ printableString = "printableString"
+ utcTime = "utcTime"
+ sequence = "sequence"
+ set = "set"
+ oidContainer = "oidContainer"
+ publicKeyPointContainer = "publicKeyPointContainer"
+
+
+_hexTagToType = {
+ "02": DerFieldType.integer,
+ "03": DerFieldType.bitString,
+ "04": DerFieldType.octetString,
+ "05": DerFieldType.null,
+ "06": DerFieldType.object,
+ "13": DerFieldType.printableString,
+ "17": DerFieldType.utcTime,
+ "30": DerFieldType.sequence,
+ "31": DerFieldType.set,
+ "a0": DerFieldType.oidContainer,
+ "a1": DerFieldType.publicKeyPointContainer,
+}
+_typeToHexTag = {v: k for k, v in _hexTagToType.items()}
+
+
+def encodeConstructed(*encodedValues):
+ return encodePrimitive(DerFieldType.sequence, "".join(encodedValues))
+
+
+def encodePrimitive(tagType, value):
+ if tagType == DerFieldType.integer:
+ value = _encodeInteger(value)
+ if tagType == DerFieldType.object:
+ value = oidToHex(value)
+ return "{tag}{size}{value}".format(tag=_typeToHexTag[tagType], size=_generateLengthBytes(value), value=value)
+
+
+def parse(hexadecimal):
+ if not hexadecimal:
+ return []
+ typeByte, hexadecimal = hexadecimal[:2], hexadecimal[2:]
+ length, lengthBytes = _readLengthBytes(hexadecimal)
+ content, hexadecimal = hexadecimal[lengthBytes: lengthBytes + length], hexadecimal[lengthBytes + length:]
+ if len(content) < length:
+ raise Exception("missing bytes in DER parse")
+
+ tagData = _getTagData(typeByte)
+ if tagData["isConstructed"]:
+ content = parse(content)
+
+ valueParser = {
+ DerFieldType.null: _parseNull,
+ DerFieldType.object: _parseOid,
+ DerFieldType.utcTime: _parseTime,
+ DerFieldType.integer: _parseInteger,
+ DerFieldType.printableString: _parseString,
+ }.get(tagData["type"], _parseAny)
+ return [valueParser(content)] + parse(hexadecimal)
+
+
+def _parseAny(hexadecimal):
+ return hexadecimal
+
+
+def _parseOid(hexadecimal):
+ return tuple(oidFromHex(hexadecimal))
+
+
+def _parseTime(hexadecimal):
+ string = _parseString(hexadecimal)
+ return datetime.strptime(string, "%y%m%d%H%M%SZ")
+
+
+def _parseString(hexadecimal):
+ return byteStringFromHex(hexadecimal).decode()
+
+
+def _parseNull(_content):
+ return None
+
+
+def _parseInteger(hexadecimal):
+ integer = intFromHex(hexadecimal)
+ bits = bitsFromHex(hexadecimal[0])
+ if bits[0] == "0": # negative numbers are encoded using two's complement
+ return integer
+ bitCount = 4 * len(hexadecimal)
+ return integer - (2 ** bitCount)
+
+
+def _encodeInteger(number):
+ hexadecimal = hexFromInt(abs(number))
+ if number < 0:
+ bitCount = 4 * len(hexadecimal)
+ twosComplement = (2 ** bitCount) + number
+ return hexFromInt(twosComplement)
+ bits = bitsFromHex(hexadecimal[0])
+ if bits[0] == "1": # if first bit was left as 1, number would be parsed as a negative integer with two's complement
+ hexadecimal = "00" + hexadecimal
+ return hexadecimal
+
+
+def _readLengthBytes(hexadecimal):
+ lengthBytes = 2
+ lengthIndicator = intFromHex(hexadecimal[0:lengthBytes])
+ isShortForm = lengthIndicator < 128 # checks if first bit of byte is 1 (a.k.a. short-form)
+ if isShortForm:
+ length = lengthIndicator * 2
+ return length, lengthBytes
+
+ lengthLength = lengthIndicator - 128 # nullifies first bit of byte (only used as long-form flag)
+ if lengthLength == 0:
+ raise Exception("indefinite length encoding located in DER")
+ lengthBytes += 2 * lengthLength
+ length = intFromHex(hexadecimal[2:lengthBytes]) * 2
+ return length, lengthBytes
+
+
+def _generateLengthBytes(hexadecimal):
+ size = len(hexadecimal) // 2
+ length = hexFromInt(size)
+ if size < 128: # checks if first bit of byte should be 0 (a.k.a. short-form flag)
+ return length.zfill(2)
+ lengthLength = 128 + len(length) // 2 # +128 sets the first bit of the byte as 1 (a.k.a. long-form flag)
+ return hexFromInt(lengthLength) + length
+
+
+def _getTagData(tag):
+ bits = bitsFromHex(tag)
+ bit8, bit7, bit6 = bits[:3]
+
+ tagClass = {
+ "0": {
+ "0": "universal",
+ "1": "application",
+ },
+ "1": {
+ "0": "context-specific",
+ "1": "private",
+ },
+ }[bit8][bit7]
+ isConstructed = bit6 == "1"
+
+ return {
+ "class": tagClass,
+ "isConstructed": isConstructed,
+ "type": _hexTagToType.get(tag),
+ }