This commit is contained in:
UrloMythus
2026-02-19 20:15:03 +01:00
parent 7785e8c604
commit cfc6bbabc9
181 changed files with 32141 additions and 4629 deletions

View File

@@ -1,27 +1,27 @@
# Author: Trevor Perrin
# See the LICENSE file for legal information regarding use of this file.
# See the LICENSE file for legal information regarding use of this file
"""Classes for reading/writing binary data (such as TLS records)."""
from __future__ import division
import sys
import struct
from struct import pack
from .compat import bytes_to_int
class DecodeError(SyntaxError):
"""Exception raised in case of decoding errors."""
pass
class BadCertificateError(SyntaxError):
"""Exception raised in case of bad certificate."""
pass
class Writer(object):
class Writer:
"""Serialisation helper for complex byte-based structures."""
def __init__(self):
@@ -32,102 +32,51 @@ class Writer(object):
"""Add a single-byte wide element to buffer, see add()."""
self.bytes.append(val)
if sys.version_info < (2, 7):
# struct.pack on Python2.6 does not raise exception if the value
# is larger than can fit inside the specified size
def addTwo(self, val):
"""Add a double-byte wide element to buffer, see add()."""
if not 0 <= val <= 0xffff:
raise ValueError("Can't represent value in specified length")
self.bytes += pack('>H', val)
def addTwo(self, val):
"""Add a double-byte wide element to buffer, see add()."""
try:
self.bytes += pack(">H", val)
except struct.error:
raise ValueError("Can't represent value in specified length")
def addThree(self, val):
"""Add a three-byte wide element to buffer, see add()."""
if not 0 <= val <= 0xffffff:
raise ValueError("Can't represent value in specified length")
self.bytes += pack('>BH', val >> 16, val & 0xffff)
def addThree(self, val):
"""Add a three-byte wide element to buffer, see add()."""
try:
self.bytes += pack(">BH", val >> 16, val & 0xFFFF)
except struct.error:
raise ValueError("Can't represent value in specified length")
def addFour(self, val):
"""Add a four-byte wide element to buffer, see add()."""
if not 0 <= val <= 0xffffffff:
raise ValueError("Can't represent value in specified length")
self.bytes += pack('>I', val)
else:
def addTwo(self, val):
"""Add a double-byte wide element to buffer, see add()."""
try:
self.bytes += pack('>H', val)
except struct.error:
raise ValueError("Can't represent value in specified length")
def addFour(self, val):
"""Add a four-byte wide element to buffer, see add()."""
try:
self.bytes += pack(">I", val)
except struct.error:
raise ValueError("Can't represent value in specified length")
def addThree(self, val):
"""Add a three-byte wide element to buffer, see add()."""
try:
self.bytes += pack('>BH', val >> 16, val & 0xffff)
except struct.error:
raise ValueError("Can't represent value in specified length")
def add(self, x, length):
"""
Add a single positive integer value x, encode it in length bytes.
def addFour(self, val):
"""Add a four-byte wide element to buffer, see add()."""
try:
self.bytes += pack('>I', val)
except struct.error:
raise ValueError("Can't represent value in specified length")
Encode positive integer x in big-endian format using length bytes,
add to the internal buffer.
if sys.version_info >= (3, 0):
# the method is called thousands of times, so it's better to extern
# the version info check
def add(self, x, length):
"""
Add a single positive integer value x, encode it in length bytes
:type x: int
:param x: value to encode
Encode positive integer x in big-endian format using length bytes,
add to the internal buffer.
:type x: int
:param x: value to encode
:type length: int
:param length: number of bytes to use for encoding the value
"""
try:
self.bytes += x.to_bytes(length, 'big')
except OverflowError:
raise ValueError("Can't represent value in specified length")
else:
_addMethods = {1: addOne, 2: addTwo, 3: addThree, 4: addFour}
def add(self, x, length):
"""
Add a single positive integer value x, encode it in length bytes
Encode positive iteger x in big-endian format using length bytes,
add to the internal buffer.
:type x: int
:param x: value to encode
:type length: int
:param length: number of bytes to use for encoding the value
"""
try:
self._addMethods[length](self, x)
except KeyError:
self.bytes += bytearray(length)
newIndex = len(self.bytes) - 1
for i in range(newIndex, newIndex - length, -1):
self.bytes[i] = x & 0xFF
x >>= 8
if x != 0:
raise ValueError("Can't represent value in specified "
"length")
:type length: int
:param length: number of bytes to use for encoding the value
"""
try:
self.bytes += x.to_bytes(length, "big")
except OverflowError:
raise ValueError("Can't represent value in specified length")
def addFixSeq(self, seq, length):
"""
Add a list of items, encode every item in length bytes
Add a list of items, encode every item in length bytes.
Uses the unbounded iterable seq to produce items, each of
which is then encoded to length bytes
which is then encoded to length bytes.
:type seq: iterable of int
:param seq: list of positive integers to encode
@@ -138,72 +87,35 @@ class Writer(object):
for e in seq:
self.add(e, length)
if sys.version_info < (2, 7):
# struct.pack on Python2.6 does not raise exception if the value
# is larger than can fit inside the specified size
def _addVarSeqTwo(self, seq):
"""Helper method for addVarSeq"""
if not all(0 <= i <= 0xffff for i in seq):
raise ValueError("Can't represent value in specified "
"length")
self.bytes += pack('>' + 'H' * len(seq), *seq)
def addVarSeq(self, seq, length, lengthLength):
"""
Add a bounded list of same-sized values.
def addVarSeq(self, seq, length, lengthLength):
"""
Add a bounded list of same-sized values
Create a list of specific length with all items being of the same
size.
Create a list of specific length with all items being of the same
size
:type seq: list of int
:param seq: list of positive integers to encode
:type seq: list of int
:param seq: list of positive integers to encode
:type length: int
:param length: amount of bytes in which to encode every item
:type length: int
:param length: amount of bytes in which to encode every item
:type lengthLength: int
:param lengthLength: amount of bytes in which to encode the overall
length of the array
"""
self.add(len(seq)*length, lengthLength)
if length == 1:
self.bytes.extend(seq)
elif length == 2:
self._addVarSeqTwo(seq)
else:
for i in seq:
self.add(i, length)
else:
def addVarSeq(self, seq, length, lengthLength):
"""
Add a bounded list of same-sized values
Create a list of specific length with all items being of the same
size
:type seq: list of int
:param seq: list of positive integers to encode
:type length: int
:param length: amount of bytes in which to encode every item
:type lengthLength: int
:param lengthLength: amount of bytes in which to encode the overall
length of the array
"""
seqLen = len(seq)
self.add(seqLen*length, lengthLength)
if length == 1:
self.bytes.extend(seq)
elif length == 2:
try:
self.bytes += pack('>' + 'H' * seqLen, *seq)
except struct.error:
raise ValueError("Can't represent value in specified "
"length")
else:
for i in seq:
self.add(i, length)
:type lengthLength: int
:param lengthLength: amount of bytes in which to encode the overall
length of the array
"""
seqLen = len(seq)
self.add(seqLen * length, lengthLength)
if length == 1:
self.bytes.extend(seq)
elif length == 2:
try:
self.bytes += pack(">" + "H" * seqLen, *seq)
except struct.error:
raise ValueError("Can't represent value in specified length")
else:
for i in seq:
self.add(i, length)
def addVarTupleSeq(self, seq, length, lengthLength):
"""
@@ -257,7 +169,7 @@ class Writer(object):
self.bytes += data
class Parser(object):
class Parser:
"""
Parser for TLV and LV byte-based encodings.
@@ -269,9 +181,6 @@ class Parser(object):
read a 4-byte integer from a 2-byte buffer), most methods will raise a
DecodeError exception.
TODO: don't use an exception used by language parser to indicate errors
in application code.
:vartype bytes: bytearray
:ivar bytes: data to be interpreted (buffer)
@@ -285,14 +194,14 @@ class Parser(object):
:ivar indexCheck: position at which the structure begins in buffer
"""
def __init__(self, bytes):
def __init__(self, data):
"""
Bind raw bytes with parser.
:type bytes: bytearray
:param bytes: bytes to be parsed/interpreted
:type data: bytearray
:param data: bytes to be parsed/interpreted
"""
self.bytes = bytes
self.bytes = data
self.index = 0
self.indexCheck = 0
self.lengthCheck = 0
@@ -307,7 +216,7 @@ class Parser(object):
:rtype: int
"""
ret = self.getFixBytes(length)
return bytes_to_int(ret, 'big')
return bytes_to_int(ret, "big")
def getFixBytes(self, lengthBytes):
"""
@@ -358,10 +267,10 @@ class Parser(object):
:rtype: list of int
"""
l = [0] * lengthList
result = [0] * lengthList
for x in range(lengthList):
l[x] = self.get(length)
return l
result[x] = self.get(length)
return result
def getVarList(self, length, lengthLength):
"""
@@ -377,13 +286,12 @@ class Parser(object):
"""
lengthList = self.get(lengthLength)
if lengthList % length != 0:
raise DecodeError("Encoded length not a multiple of element "
"length")
raise DecodeError("Encoded length not a multiple of element length")
lengthList = lengthList // length
l = [0] * lengthList
result = [0] * lengthList
for x in range(lengthList):
l[x] = self.get(length)
return l
result[x] = self.get(length)
return result
def getVarTupleList(self, elemLength, elemNum, lengthLength):
"""
@@ -402,8 +310,7 @@ class Parser(object):
"""
lengthList = self.get(lengthLength)
if lengthList % (elemLength * elemNum) != 0:
raise DecodeError("Encoded length not a multiple of element "
"length")
raise DecodeError("Encoded length not a multiple of element length")
tupleCount = lengthList // (elemLength * elemNum)
tupleList = []
for _ in range(tupleCount):