Ошибка переполнения base85 при декодировании закодированной строки base85

Мне нужно встроить двоичные данные в файлы XML, поэтому я решил использовать для этого кодировку base85.

У меня есть большой байтовый массив, который заполнен выводом вызовов struct.pack() через bytearray.extend(struct.pack(varying_data)), Затем он сжимается с zlib и закодированы с base64.b85encode(),

Это работало все время, но в одном входном файле есть следующая странная ошибка:

ValueError: base85 overflow in hunk starting at byte 582200`

Затем я изменил base64.py, чтобы распечатать, какое значение имеет текущий чанк и из каких байтов он состоит. Блок ввода b'||a|3' и его значение равно 4.331.076.573, что больше 256^4 = 4.294.967.296 и, следовательно, не может быть представлено четырьмя байтами (отсюда и ошибка).

Но я не понимаю, как это может произойти?

Это важная часть кода:

elif isinstance(self.content, (bytes, bytearray)):
    base85 = zlib.compress(self.content, 9)

    # pad=False doesn't make a difference here
    base85 = base64.b85encode(base85, pad=True).decode()

    base85 = escape_xml(base85)

    file.write(base85)
def escape_xml(text):

    text = text.replace("&", "&")
    text = text.replace("<", "&lt;")
    text = text.replace(">", "&gt;")
    text = text.replace("\"", "&quot;")
    text = text.replace("'", "&apos;")

    return text

И код для декодирования:

def decode_binary_data(data):
    data = unescape_xml(data)

    # Remove newline for mixed content support (does not apply in this case)
    data = data.split("\n", 1)[0]

    # Error!
    data = base64.b85decode(data)

    return zlib.decompress(data)

def unescape_xml(text):
    text = text.replace("&quot;", "\"")
    text = text.replace("&apos;", "'")
    text = text.replace("&lt;", "<")
    text = text.replace("&gt;", ">")
    text = text.replace("&amp;", "&")

    return text

Base85 может теоретически работать с 85^5 = 4.437.053.125 возможных комбинаций, но, поскольку он получает данные из байтов, мне интересно, как это вообще возможно. Это происходит от сжатия? Это не должно быть проблемой, так как кодирование и декодирование должны быть симметричными. Если это проблема, как сжать данные в любом случае?

Выбор Ascii85 вместо (a84encode()) работает, но я думаю, что это на самом деле не решает проблему, может быть, это не помогает в других случаях?

Спасибо за помощь!

1 ответ

Решение

Я нашел проблему! Здесь не проблема ни алгоритма base85, ни сжатия. Это XML.

Для экспорта / записи XML с включенной строкой base85 я написал свой собственный класс и функции для экспорта XML, чтобы он выглядел красиво (xml.etree.ElementTree записывает все в одну строку, и для этого проекта я не могу использовать внешние пакеты из pip). Вот почему строка base85 должна быть экранирована вручную.

Но для чтения файлов XML я использую xml.etree.ElementTree, Я не знал, что большинство XML-библиотек (не) экранируют строки автоматически (что имеет смысл).

Итак, проблема заключалась в том, что ручное удаление ElementTree делает автоматически. В результате строка base85 была дважды удалена. А так как алфавит base85 содержит каждую букву, включенную в escape-строки XML ($amp;, $lt; и т.д.), и с более чем 500000 символов в этой строке base85, вполне вероятно, что в выходной строке есть комбинация символов, которая образует допустимую escape-строку XML.

И это было проблемой. &lt; содержался в строке unescaped base85 и снова получался unescaped, что приводило к смещению всех следующих байтов, которое привело к этой ошибке.

Я много работаю с LabView, Python и javascript, и мне пришлось создать свои собственные процедуры кодирования и декодирования Base85 для LabView, у которых есть только контрольная сумма MD5. Для шифрования или хорошей обфускации вам нужно накатить собственное. Возможно, в будущих версиях LabView в библиотеке будет Base85.

Я хочу сказать, что теперь у меня есть все 3 вкуса base85. Ascii85, Base85 и Z85. У каждого есть уникальный набор символов, который он использует при преобразовании из base10 в base85. Каждая версия может быть сбита (поврежден вывод) такими вещами, как управляющие символы, слишком много пробелов в строке, тяжелые символы, такие как HTML и XML, символы более 126 (тильда).

Для безопасного кодирования больших текстовых файлов, особенно многострочных файлов и файлов с большим количеством символов, мне просто нужно, чтобы код распознал все эти потенциальные проблемы и сначала преобразовал их в шестнадцатеричные. Да, он удваивает количество символов, но двигатель base10 - base85 не выйдет из строя. Даже для больших текстовых файлов Z85 вылетал после 1000 символов или около того, и проблема заключалась в карте символов Z85, в которой символы расположены вне десятичного порядка, поэтому на длинных строках могло произойти переполнение. Для своих целей я изменил карту символов Z85, чтобы символы были в десятичном порядке, и теперь Z85 больше не дает сбой при работе с большими файлами.

Ascii85, Base85 и Z85 подвержены сбоям из-за тех же проблем, упомянутых выше, независимо от того, написаны ли они на python, javascript или LabView. Часто это несколько последовательных символов / пробелов, которые вызывают математическое переполнение, поэтому вывод искажается и не может быть декодирован.

ПРИМЕЧАНИЕ. Очень важно дополнить ваши строки так, чтобы они делились на 4, а при декодировании добавляйте хеш-строку с 'u' или знаком тильды, чтобы хеш делился на 5.

Другие вопросы по тегам