Ошибка переполнения 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("<", "<")
text = text.replace(">", ">")
text = text.replace("\"", """)
text = text.replace("'", "'")
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(""", "\"")
text = text.replace("'", "'")
text = text.replace("<", "<")
text = text.replace(">", ">")
text = text.replace("&", "&")
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.
И это было проблемой. <
содержался в строке 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.