diff options
author | S. Solomon Darnell | 2025-03-28 21:52:21 -0500 |
---|---|---|
committer | S. Solomon Darnell | 2025-03-28 21:52:21 -0500 |
commit | 4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch) | |
tree | ee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/ellipticcurve/utils/der.py | |
parent | cc961e04ba734dd72309fb548a2f97d67d578813 (diff) | |
download | gn-ai-master.tar.gz |
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.py | 159 |
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), + } |