Тема 08

Криптографические хеш-функции

Криптографические хеш-функции

Хеш-функция сжимает данные произвольной длины в значение фиксированной длины — 256 или 512 бит — и делает это так, чтобы по значению нельзя было восстановить вход. Это самый используемый примитив в криптографии: он нужен для проверки целостности файлов, для электронной подписи (подписывается хеш, а не сообщение), для кодов аутентификации, для деревьев Меркла в Git и Certificate Transparency, для хранения паролей и для доказательств работы в блокчейне.

Требования к хеш-функции формулируются иначе, чем к шифру. Шифр должен скрывать содержимое; хеш ничего не скрывает, потому что он необратим, — но он должен быть стойким к трём видам атак: к нахождению прообраза, к нахождению второго прообраза и к нахождению коллизий. Эти три требования дают разные оценки сложности (2^n, 2^n и 2^(n/2)), и путать их — типичная ошибка. Именно из-за коллизий, а не из-за обращения, пали MD5 и SHA-1: их прообраз остаётся трудным, а коллизии нашлись.

Тема разбирает устройство хеш-функций от конструкции Меркла — Дамгора до губки Keccak, историю взломов MD5 и SHA-1, современные функции (SHA-2, SHA-3, BLAKE2, Стрибог) и применения, где хеш необходим, но недостаточен. Последний раздел — о том, чего хеш не даёт: он не обеспечивает аутентичность (для этого нужен MAC), он не годится для хранения паролей (для этого нужны медленные функции, тема 10) и он не защищает от подмены, если используется как «хеш вместо MAC».

Паспорт темы. Нужно знать: темы 0, 2. Освоите: формулировать три свойства хеш-функции и оценки 2^n и 2^(n/2); объяснять конструкцию Меркла — Дамгора и атаку удлинения; различать SHA-2, SHA-3 и XOF; строить дерево Меркла; указывать, чего хеш не даёт. Проверяет: ЛР 8. Минимум: разделы 1–4. Углубление: разделы 5–6.

Определение и свойства

Три требования стойкости

Криптографическая хеш-функция H отображает строки произвольной длины в строки фиксированной длины n и должна удовлетворять трём свойствам.

Стойкость к нахождению прообраза (англ. preimage resistance): по заданному значению h вычислительно невозможно найти такой m, что H(m) = h. Лучший известный метод — перебор, сложность 2^n. Это свойство нужно, когда хеш используется как обязательство: показав h, сторона позже раскрывает m, и никто не может подобрать другое m' с тем же хешем.

Стойкость к нахождению второго прообраза (англ. second preimage resistance): по заданному m невозможно найти m' ≠ m с H(m') = H(m). Сложность тоже 2^n для идеальной функции. Это свойство нужно для целостности: если злоумышленник может подменить файл, сохранив хеш, проверка контрольных сумм бесполезна.

Стойкость к коллизиям (англ. collision resistance): невозможно найти пару m ≠ m' с H(m) = H(m'). Здесь сложность падает до 2^(n/2) из-за парадокса дней рождения. Это свойство нужно для подписи: если можно найти две разные документы с одинаковым хешем, подпись под одним переносится на другой.

Соотношения между свойствами несимметричны. Стойкость к коллизиям влечёт стойкость ко второму прообразу (если можно найти второй прообраз, то и коллизию — с этим m), но не наоборот. Стойкость к прообразу не следует из стойкости к коллизиям. Поэтому для подписи требуется именно стойкость к коллизиям, и именно она определяет требования к длине: 128-битный хеш даёт только 64 бита стойкости к коллизиям, что недостаточно.

Парадокс дней рождения

Парадокс дней рождения — не парадокс, а свойство вероятностей. Если в комнате 23 человека, вероятность совпадения дней рождения больше 50 %. Причина в том, что сравниваются не дни рождения с фиксированной датой, а все пары: при 23 людях пар 253.

Формально вероятность отсутствия коллизии при k случайных значениях из множества размера N равна

P(\text{нет коллизии}) \approx \exp\left(-\frac{k(k-1)}{2N}\right)

При k \approx 1{,}18 \sqrt{N} вероятность коллизии достигает 50 %, а ожидаемое число значений до первой коллизии — около 1{,}25 \sqrt{N}. Для N = 2^n это 2^{n/2}.

Практическое следствие для хеш-функций: 128-битный хеш (MD5) даёт коллизию за 2^64 операций, что для хорошо финансируемого противника достижимо; 160-битный (SHA-1) — за 2^80, что тоже стало достижимым; 256-битный — за 2^128, что нет.

Схема ниже показывает, как растёт вероятность коллизии с числом значений и почему порог наступает задолго до исчерпания пространства.

Парадокс дней рождения
Вероятность коллизии достигает половины при числе значений около 1,18·√N; для 256-битного хеша это 2^128, а не 2^256

Числовой пример: для усечённого до 24 бит SHA-256 среднее число попыток до коллизии, измеренное на 200 испытаниях, составило около 5 тысяч, что близко к теоретическому 1,25·2^12 ≈ 5100. Для 32 бит измеренное среднее на 12 испытаниях — около 66 тысяч при теоретическом 1,25·2^16 ≈ 82 000; разброс велик, потому что распределение времени до коллизии имеет тяжёлый хвост.

Стоимость атак и уровни стойкости

Из требований следуют уровни стойкости, которые нужно сопоставлять с длиной выхода.

Длина выходаСтойкость к прообразуСтойкость к коллизиямСтатус
128 бит (MD5)2^1282^64взломан, коллизии за секунды
160 бит (SHA-1)2^1602^80взломан, коллизия найдена в 2017
256 бит (SHA-256)2^2562^128действующий стандарт
512 бит (SHA-512)2^5122^256действующий стандарт
256 бит (SHA3-256)2^2562^128действующий стандарт
256 бит (Стрибог-256)2^2562^128ГОСТ Р 34.11-2012

Отсюда практическое правило: для электронной подписи нужен хеш с выходом не меньше 256 бит, потому что 128 бит стойкости к коллизиям — минимальный приемлемый уровень. SHA-256 в этом смысле стоит на нижней границе: его 128-битная стойкость к коллизиям соответствует уровню AES-128.

Отдельно стоит отметить атаку на длинные сообщения: для конструкций Меркла — Дамгора существует атака второго прообраза сложности 2^(n/2) вместо 2^n, если сообщение очень длинное 13. Это не ломает SHA-256 практически, но показывает, что «идеальные» оценки выполняются не для всех конструкций.

Конструкция Меркла — Дамгора

Функция сжатия и дополнение

Хешировать сообщение произвольной длины одной функцией неудобно, поэтому конструкция разбивает задачу на две: функция сжатия (англ. compression function) обрабатывает блок фиксированной длины и текущее состояние, а режим обхода (англ. mode of operation) связывает блоки.

В конструкции Меркла — Дамгора 43 состояние h обновляется блок за блоком:

h₀ = IV
hᵢ = f(hᵢ₋₁, Mᵢ)
H(M) = h_k

Функция f называется функцией сжатия; она принимает состояние и блок, а возвращает новое состояние. IV — фиксированные константы, определённые стандартом.

Чтобы конструкция была корректной, сообщение нужно дополнить до кратности длине блока и включить в дополнение длину исходного сообщения. Иначе два сообщения разной длины с одинаковым «хвостом» могли бы дать одинаковый хеш. Стандартное дополнение: бит 1, затем нули, затем 64 или 128 бит длины.

Схема ниже показывает цепочку сжатия и место дополнения.

Конструкция Меркла — Дамгора
Сообщение дополняется до кратности длине блока с указанием длины, затем блоки последовательно сжимаются с состоянием; итоговое состояние и есть хеш

Главное достоинство конструкции — возможность доказать стойкость: если функция сжатия стойка к коллизиям, то и вся хеш-функция стойка к коллизиям. Это теорема Меркла — Дамгора, и именно она объясняет, почему такие функции так популярны: безопасность сводится к свойствам небольшого компонента.

Главный недостаток — атака удлинения сообщения, о которой ниже.

Атака удлинения сообщения

Атака удлинения (англ. length extension attack) следует прямо из устройства: состояние после обработки M и есть H(M). Значит, зная H(M) и длину M, но не зная M, противник может продолжить вычисление с этого состояния, добавив дополнение и новый блок. Формально он может вычислить H(M || pad || X) для произвольного X, не зная M.

def length_extension(digest, original_len, suffix):
    state = state_from_digest(digest)          # восемь 32-битных слов из хеша
    glue = padding(original_len)               # дополнение, которое уже «съел» оригинал
    forged_len = original_len + len(glue)
    return sha256(suffix, state=state, prefix_len=forged_len), glue + suffix

Для криптографической хеш-функции это не уязвимость: свойство «хеш от M совпадает с состоянием» не считается недостатком. Уязвимость возникает, когда хеш используют как код аутентификации: tag = SHA256(key || message). Противник получает tag, применяет удлинение и получает валидный тег для message || pad || suffix, не зная ключа. Именно так ломаются самодельные MAC на основе SHA-256 и SHA-1, и именно поэтому существует HMAC — конструкция, специально устойчивая к удлинению (тема 9).

Защита бывает трёх видов: использовать HMAC, использовать хеш с губкой (SHA-3, BLAKE2), где удлинение невозможно по построению, или ставить ключ в конец сообщения (менее удачно, потому что требует другого анализа и плохо сочетается с потоковой обработкой).

Почему губка устроена иначе

Губка (англ. sponge construction) применяет к состоянию две функции: перестановку f и выборку. Состояние делится на две части — скорость (rate) r и ёмкость (capacity) c, — и данные поглощаются по r бит за раз.

Работа состоит из двух фаз. Фаза поглощения (absorbing): блок сообщения складывается с частью состояния, затем применяется перестановка. Фаза отжима (squeezing): из части состояния выдаются биты, затем применяется перестановка, и так до нужной длины.

Схема ниже показывает обе фазы.

Губка Keccak
Сообщение поглощается по r бит с перемешиванием перестановкой, затем выход отжимается из скорости; ёмкость не участвует в выходе и обеспечивает стойкость

Почему это решает проблему удлинения: состояние никогда не выдаётся целиком, а ёмкость c остаётся скрытой. Зная выход, противник не знает состояние и не может продолжить вычисление.

Параметры: для SHA3-256 состояние 1600 бит, скорость 1088, ёмкость 512. Стойкость к коллизиям ограничена 2^(c/2) = 2^256, что соответствует 256-битному выходу; стойкость к прообразу — 2^c = 2^512 для полного состояния, но не меньше 2^n по выходу.

История взломов: MD5 и SHA-1

MD5

MD5 (1992) — 128-битная хеш-функция Рона Ривеста, наследник MD4. Долгое время считалась надёжной, и в 1990-х её использовали для подписи, контрольных сумм и хранения паролей.

В 2004 году Ван Сяоюнь, Фэн Дэнгуо и Лай Хуэйвэй объявили о нахождении коллизий MD5, а в 2005 году вышла работа «How to Break MD5 and Other Hash Functions» с полным описанием метода 7. Атака использовала дифференциальные характеристики и находила коллизии за часы на обычном компьютере, а позже — за секунды.

Практические последствия не заставили себя ждать. В 2008 году был создан поддельный центр сертификации с помощью коллизии MD5. В 2012 году вредонос Flame использовал выбранно-префиксную коллизию MD5, чтобы подделать подпись обновления Microsoft — это была атака на целую цепочку доверия, а не на отдельный файл.

Сегодня MD5 используется только для несекьюрных задач: контрольные суммы для обнаружения случайных повреждений, ключи кэша, отпечатки файлов. Для криптографии он непригоден.

SHA-1

SHA-1 (1995) — 160-битная функция, наследник SHA-0. Её 80-битная стойкость к коллизиям долго считалась достаточной.

В 2005 году Ван, Инь и Юй описали теоретическую атаку сложности 2^69 вместо 2^80 5. Это ещё не была практическая коллизия, но запас сократился и стало ясно, что нужно переходить на SHA-2.

В 2017 году группа SHAttered нашла первую реальную коллизию SHA-1: два разных PDF-файла с одинаковым SHA-1 6. Стоимость — около 2^63,1 вычислений SHA-1, что эквивалентно 6500 годам работы одного процессора и 110 годам работы видеокарты. Это была демонстрация, а не повседневная атака, но она показала, что 80-битная стойкость исчерпана.

В 2019–2020 годах Леран и Пейрен построили выбранно-префиксную коллизию SHA-1 (проект «SHA-1 is a Shambles») 8, которая позволяет подделать документ с заданным префиксом — например, сертификат или ключ PGP. Сложность около 2^63,4, стоимость — десятки тысяч долларов на арендованных GPU.

После этого SHA-1 окончательно признан непригодным для подписей, и все основные браузеры и удостоверяющие центры отказались от сертификатов с SHA-1.

Что из этого следует

Общий вывод из истории MD5 и SHA-1: запас стойкости должен быть больше, чем кажется достаточным на момент проектирования. 128 бит считались избыточными в 1992 году, 160 — в 1995-м. Оба алгоритма пали не из-за обращения, а из-за коллизий, и оба — из-за конструкции, а не из-за длины выхода как таковой: SHA-1 с 160 битами имел 80-битную стойкость к коллизиям, и именно она оказалась недостаточной.

Второй вывод: практические атаки приходят позже теоретических. Теоретическая атака SHA-1 была опубликована в 2005 году, практическая коллизия — в 2017-м. Двенадцать лет — типичный разрыв между «сломано в теории» и «сломано в жизни», и всё это время алгоритм оставался в протоколах.

Третий вывод: выбранно-префиксные коллизии опаснее обычных. Обычная коллизия даёт два «мусорных» файла с одинаковым хешем; выбранно-префиксная позволяет взять осмысленный документ и подобрать к нему другой осмысленный с тем же хешем. Именно она делает атаку применимой к сертификатам и подписям.

Современные функции

SHA-2 и SHA-256 по шагам

SHA-2 — семейство, опубликованное в 2001 году и стандартизованное в FIPS 180-4 1. В него входят SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224 и SHA-512/256. Все построены по конструкции Меркла — Дамгора с функцией сжатия на основе схемы, похожей на ARX.

SHA-256: блок 512 бит, состояние 8 слов по 32 бита, 64 раунда, длина выхода 256 бит. В каждом раунде используются:

  • сумма по модулю 2^32 с вращениями (Σ₀, Σ₁);
  • функция выбора Ch(x, y, z) = (x & y) ^ (~x & z);
  • функция большинства Maj(x, y, z) = (x & y) ^ (x & z) ^ (y & z);
  • константы Kᵢ — первые 32 бита дробных частей кубических корней первых 64 простых чисел.

Начальные значения H₀…H₇ — первые 32 бита дробных частей квадратных корней первых восьми простых чисел. Происхождение констант проверяемо (их можно пересчитать), что и является аргументом «nothing up my sleeve».

def compress(state, block):
    w = list(struct.unpack(">16I", block)) + [0] * 48
    for i in range(16, 64):
        s0 = rotr(w[i - 15], 7) ^ rotr(w[i - 15], 18) ^ (w[i - 15] >> 3)
        s1 = rotr(w[i - 2], 17) ^ rotr(w[i - 2], 19) ^ (w[i - 2] >> 10)
        w[i] = (w[i - 16] + s0 + w[i - 7] + s1) & 0xFFFFFFFF
    a, b, c, d, e, f, g, h = state
    for i in range(64):
        S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25)
        ch = (e & f) ^ (~e & g)
        t1 = (h + S1 + ch + K[i] + w[i]) & 0xFFFFFFFF
        S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22)
        maj = (a & b) ^ (a & c) ^ (b & c)
        t2 = (S0 + maj) & 0xFFFFFFFF
        h, g, f, e, d, c, b, a = g, f, e, (d + t1) & 0xFFFFFFFF, c, b, a, (t1 + t2) & 0xFFFFFFFF
    return tuple((x + y) & 0xFFFFFFFF for x, y in zip(state, (a, b, c, d, e, f, g, h)))

Проверка: SHA256("abc") = ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad, SHA256("") = e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855. Первый вектор — из стандарта, второй полезен тем, что проверяет обработку пустого сообщения: дополнение всё равно добавляет блок.

printf 'abc' | sha256sum
printf 'abc' | openssl dgst -sha256

Обе команды дают одно и то же значение. Разница в выводе: sha256sum печатает хеш, пробел и имя файла, openssl dgst — имя алгоритма и хеш.

SHA-3 и губка Keccak

SHA-3 появился не потому, что SHA-2 сломали, а потому что нужен был запасной вариант с другой конструкцией. Конкурс NIST (2007–2012) выиграл Keccak, стандартизованный в FIPS 202 29.

Keccak использует состояние 1600 бит (5×5 слов по 64 бита) и перестановку из 24 раундов, каждый из которых состоит из пяти шагов: θ, ρ, π, χ, ι. Нелинейность даёт χ — единственный нелинейный шаг; остальные обеспечивают диффузию.

Параметры вариантов: для SHA3-256 скорость 1088 бит, ёмкость 512; для SHA3-512 скорость 576, ёмкость 1024. Чем больше ёмкость, тем выше стойкость и тем меньше скорость — данных за одну перестановку обрабатывается меньше.

Отдельное достоинство семейства — XOF (англ. extendable output function): SHAKE128 и SHAKE256 могут выдать выход любой длины из одного входа. Это удобно для выработки ключей и для схем, где нужно много псевдослучайных байт.

Практически SHA-3 медленнее SHA-2 на большинстве процессоров (нет аппаратных инструкций, кроме специализированных), но даёт независимую конструкцию: если завтра найдут атаку на Меркла — Дамгора, SHA-3 останется.

BLAKE2, BLAKE3 и Стрибог

BLAKE2 (2013) — оптимизированная замена MD5 и SHA-1: быстрее SHA-2 на программных платформах, с поддержкой ключа и персонализации 10. BLAKE2b даёт 512 бит, BLAKE2s — 256. BLAKE3 (2020) — дальнейшее развитие с древовидным режимом и параллелизмом; она используется там, где скорость важнее консерватизма стандарта.

Стрибог — российский стандарт ГОСТ Р 34.11-2012, с выходами 256 и 512 бит 11. Построен по той же идее «сжатие плюс итерация», но с нелинейным преобразованием LPS (подстановки, перестановка, линейное преобразование) и 12 раундами. Стандарт определяет и функцию сжатия, и процедуру дополнения.

ФункцияВыходБлокРаундыКонструкцияОсобенность
SHA-25625651264Меркла — Дамгорастандарт де-факто
SHA-512512102480Меркла — Дамгорабыстрее на 64-битных
SHA3-2562561088 (rate)24губканезависимая конструкция
SHAKE256любая1088 (rate)24губкаXOF
BLAKE2bдо 512102412HAIFAочень быстрая
BLAKE3любая10247деревопараллельная
Стрибог-25625651212LPSГОСТ Р 34.11-2012

Применения

Целостность файлов и дедупликация

Простейшее применение — контрольная сумма: sha256sum file даёт значение, которое можно сверить после передачи. Это защищает от случайных повреждений и от подмены только в том случае, если значение получено по независимому каналу. Если противник может подменить и файл, и контрольную сумму, проверка бесполезна — именно поэтому дистрибутивы публикуют хеши и подписи отдельно, а пакетные менеджеры проверяют подпись репозитория, а не только хеш пакета.

Дедупликация в системах резервного копирования и в контентных хранилищах (Git, Docker) использует хеш как адрес объекта: одинаковое содержимое даёт одинаковый адрес, и объект хранится один раз. Это работает, пока хеш стойкий к коллизиям: при коллизии два разных объекта получили бы один адрес, и содержимое подменилось бы. Практически Git использует SHA-1 и уже переходит на SHA-256 именно из-за этого риска.

Дерево Меркла

Дерево Меркла (англ. Merkle tree) решает задачу доказательства включения: как убедить проверяющего, что элемент входит в набор, не передавая весь набор.

Листья — хеши элементов, узлы — хеши конкатенации двух дочерних хешей, корень — хеш всего набора. Чтобы доказать включение элемента, достаточно передать путь от листа до корня — log₂ N хешей вместо N элементов.

Схема ниже показывает дерево и путь доказательства.

Дерево Меркла
Листья — хеши элементов, внутренние узлы — хеши пар; доказательство включения состоит из log₂N соседних хешей

Применения: Git (каждый коммит — дерево объектов), Certificate Transparency (лог сертификатов с деревом Меркла и доказательствами включения, RFC 6962 12), блокчейн (корень дерева транзакций в заголовке блока), системы обновлений (проверка, что файл входит в подписанный набор).

def merkle_root(leaves):
    level = [leaf_hash(x) for x in leaves]
    while len(level) > 1:
        if len(level) % 2:
            level.append(level[-1])       # нечётный узел дублируется
        level = [node_hash(level[i], level[i + 1]) for i in range(0, len(level), 2)]
    return level[0]

Тонкость с дублированием нечётного узла: если дублировать последний элемент, возникает уязвимость «второго прообраза» для дерева — два разных набора могут дать один корень. Поэтому в серьёзных реализациях (Certificate Transparency) дублирование заменяют подъёмом узла на уровень выше либо добавляют в хеш длину и позицию. Это хороший пример того, что дерево Меркла — не «просто хеши», а конструкция с собственными тонкостями.

Обязательства и доказательства работы

Обязательство (англ. commitment) — схема, позволяющая «запечатать» значение: commit = H(value || nonce). Позже сторона раскрывает value и nonce, и все могут проверить, что обязательство было дано именно для этого значения. Свойства — сокрытие (по commit нельзя узнать value) и связывание (нельзя найти другое value с тем же commit).

Доказательство работы (англ. proof of work) — поиск такого nonce, чтобы H(block || nonce) начиналось с заданного числа нулевых бит. Это задача поиска частичного прообраза: сложность 2^k для k ведущих нулей, проверка — один хеш. Используется в блокчейне для согласования и в антиспам-системах как «вычислительный налог».

def solve(data, k):
    nonce = 0
    while True:
        digest = hashlib.sha256(data + nonce.to_bytes(8, "big")).digest()
        if int.from_bytes(digest, "big") >> (256 - k) == 0:
            return nonce, digest
        nonce += 1

Ожидаемое число попыток — 2^k. Для k = 20 это около миллиона хешей, для k = 32 — около четырёх миллиардов.

Чего хеш не даёт

Аутентичность и MAC

Хеш не обеспечивает аутентичность. Если злоумышленник может изменить и сообщение, и хеш, проверка пройдёт. Более того, даже если хеш передан по защищённому каналу, конструкция SHA256(key || message) уязвима к удлинению, как показано выше.

Для аутентичности нужен код аутентификации сообщения (MAC), который использует ключ и не позволяет вычислить тег без него. Стандартный выбор — HMAC: HMAC(K, m) = H((K ⊕ opad) || H((K ⊕ ipad) || m)), конструкция, устойчивая к удлинению по построению (тема 9).

Хранение паролей

Хеш не годится для хранения паролей по двум причинам. Первая: он быстрый, а значит перебор паролей по словарю идёт с огромной скоростью — миллиарды вариантов в секунду на GPU. Вторая: без соли одинаковые пароли дают одинаковые хеши, что позволяет строить радужные таблицы и находить пароли массово.

Правильное решение — медленные функции с солью: PBKDF2 с большим числом итераций, scrypt, Argon2id (тема 10). Они специально спроектированы так, чтобы каждая попытка перебора стоила дорого.

Другие типичные ошибки

Ещё несколько ошибок встречаются регулярно. Использование усечённого хеша (например, первых 8 байт) без понимания, что стойкость к коллизиям падает до 2^32. Сравнение хешей не в постоянном времени — утечка через время сравнения. Хеширование вместе с данными, которые противник контролирует (что позволяет подобрать коллизию). Использование MD5 или SHA-1 «для совместимости» в системе, где безопасность важна.

Итоги темы

Хеш-функция сжимает данные произвольной длины в значение фиксированной длины и должна быть стойкой к трём видам атак: к прообразу, ко второму прообразу и к коллизиям. Оценки сложности различаются — 2^n для первых двух и 2^(n/2) для коллизий, — и именно коллизии определяют требования к длине выхода: для подписи нужен хеш не короче 256 бит.

Конструкция Меркла — Дамгора сводит стойкость всей функции к стойкости функции сжатия, но порождает атаку удлинения сообщения, из-за которой SHA256(key || message) не является MAC. Губка Keccak решает проблему иначе: состояние никогда не выдаётся целиком, ёмкость скрыта, и удлинение невозможно по построению.

История MD5 и SHA-1 показывает, как быстро исчерпывается запас: MD5 пал в 2004–2005 годах, SHA-1 — в 2017-м, причём оба — из-за коллизий, а не из-за обращения. Практические атаки приходят через годы после теоретических, и всё это время алгоритмы остаются в протоколах.

Современный выбор: SHA-256 или SHA-512 для совместимости, SHA-3 как независимая конструкция, BLAKE2/BLAKE3 для скорости, Стрибог для соответствия ГОСТ. Деревья Меркла дают эффективные доказательства включения и лежат в основе Git, Certificate Transparency и блокчейна.

Но хеш не решает задачу аутентичности, и это ключевой вывод. Следующая тема переходит к кодам аутентификации сообщений и аутентифицированному шифрованию — механизмам, которые отвечают именно на вопрос «данные подлинные?», и разбирает, почему HMAC устроен так, чтобы не повторить ошибку с удлинением.

Литература

  1. NIST. Secure Hash Standard (SHS). — FIPS 180-4, 2015. https://doi.org/10.6028/NIST.FIPS.180-4
  2. NIST. SHA-3 Standard: Permutation-Based Hash and Extendable-Output Functions. — FIPS 202, 2015. https://doi.org/10.6028/NIST.FIPS.202
  3. Merkle R. C. A Certified Digital Signature. — Advances in Cryptology — CRYPTO '89, Springer, 1990, С. 218–238. DOI: 10.1007/0-387-34805-0_21
  4. Damgård I. B. A Design Principle for Hash Functions. — Advances in Cryptology — CRYPTO '89, Springer, 1990, С. 416–427. DOI: 10.1007/0-387-34805-0_17
  5. Wang X., Yin Y. L., Yu H. Finding Collisions in the Full SHA-1. — Advances in Cryptology — CRYPTO 2005, Springer, 2005, С. 17–36. DOI: 10.1007/11535218_2
  6. Stevens M., Bursztein E., Karpman P., Albertini A., Markov Y. The First Collision for Full SHA-1. — Advances in Cryptology — CRYPTO 2017, Springer, 2017, С. 570–596. DOI: 10.1007/978-3-319-63688-7_19
  7. Wang X., Yu H. How to Break MD5 and Other Hash Functions. — Advances in Cryptology — EUROCRYPT 2005, Springer, 2005, С. 19–35. DOI: 10.1007/11426639_2
  8. Leurent G., Peyrin T. From Collisions to Chosen-Prefix Collisions: Application to Full SHA-1. — Advances in Cryptology — EUROCRYPT 2019, Springer, 2019, С. 527–555. DOI: 10.1007/978-3-030-17659-4_18
  9. Bertoni G., Daemen J., Peeters M., Van Assche G. Cryptographic Sponge Functions. — Keccak reference, 2011. https://keccak.team/files/SpongeFunctions.pdf
  10. Aumasson J.-P., Neves S., Wilcox-O'Hearn Z., Winnerlein C. BLAKE2: Simpler, Smaller, Fast as MD5. — Applied Cryptography and Network Security (ACNS 2013), Springer, 2013, С. 119–135. DOI: 10.1007/978-3-642-38980-1_8
  11. ГОСТ Р 34.11-2012. Информационная технология. Криптографическая защита информации. Функция хеширования. — Стандартинформ, 2013.
  12. Laurie B., Langley A., Kasper E. Certificate Transparency. — RFC 6962, IETF, 2013. https://www.rfc-editor.org/rfc/rfc6962
  13. Kelsey J., Schneier B. Second Preimages on n-Bit Hash Functions for Much Less than 2ⁿ Work. — Advances in Cryptology — EUROCRYPT 2005, Springer, 2005, С. 474–490. DOI: 10.1007/11426639_28