Keccak-256 в Ethereum: как работает хеш-функция и чем она отличается от SHA-3

Keccak-256 (keccak256) — криптографическая хеш-функция, на которой держится почти весь «технический клей» Ethereum: от вычисления адресов и селекторов функций до хеширования данных в хранилище и построения деревьев. В байткоде EVM она скрыта за опкодом SHA3, а в Solidity вызывается встроенной функцией keccak256(...).

Важно: Keccak-256 в Ethereum не полностью совпадает со стандартизованным NIST SHA3-256. Это разные варианты одной и той же семейства Keccak с отличающимся паддингом, поэтому их результаты несовместимы.

Keccak-256 в Ethereum: как работает хеш-функция и чем она отличается от SHA-3

Что такое Keccak-256

Keccak — семейство губчатых (sponge) хеш-функций, которое легло в основу стандарта SHA-3. Конкретная конфигурация с 256-битным выводом называется Keccak-256.

Ключевые свойства:

  • Фиксированная длина вывода — 256 бит (32 байта).
  • Вход может быть любой длины.
  • Односторонность и устойчивость к коллизиям на современном уровне безопасности.
  • Удобство для построения других примитивов (MAC, PRF и т.д.) на той же губчатой конструкции.

В Ethereum под именем keccak256 используется именно вариант Keccak-256 с «достандартным» паддингом, который применяли до того, как NIST финализировал SHA-3. Это и создаёт различия с SHA3-256 в библиотеках.

Keccak-256 vs SHA-3: в чём отличие

Формулировка «Keccak-256 — это SHA-3» некорректна. Более точная версия:

  • Исходная конструкция у Keccak-256 и SHA3-256 одна и та же (семейство Keccak).
  • Параметры паддинга и доменного разделения отличаются — соответственно, на одни и те же входные данные вы получите разные хеши.
  • NIST, стандартизируя SHA-3, введёл дополнительные доменные суффиксы, чтобы отделить хеш-функции от других режимов.

На практике это означает:

  • Если в библиотеке есть и keccak256, и sha3_256, нельзя использовать их как взаимозаменяемые.
  • В мире Ethereum «правильным» считается именно Keccak-256 в его «эфириумной» конфигурации — им считают адреса, селекторы и события.
  • При интеграции с внешними системами (например, другими блокчейнами или серверным бекендом) важно явно указывать, какой именно вариант Keccak/SHA-3 используется.

Где используется keccak256 в Ethereum

Keccak-256 — базовый кирпич в нескольких важных механизмах протокола и EVM:

  • Адреса аккаунтов и контрактов
    • Адрес EOA — это последние 20 байт от keccak256(public_key) (не сжатый публичный ключ после ECDSA).
    • Адрес контракта при CREATE — берётся из keccak256(RLP(sender, nonce)), откуда также берут последние 20 байт.
    • При CREATE2 адрес считается по формуле keccak256(0xff, deployer, salt, keccak256(init_code)) — см. CREATE2 и детерминированные адреса контрактов.
  • Function selector
    • Сигнатура функции записывается строкой вида "transfer(address,uint256)".
    • keccak256 от этой строки вычисляется в 32-байтовый хеш.
    • Первые 4 байта этого хеша — и есть function selector, который подставляется в calldata.
  • События (events)
    • Аналогично функциям, хешируется строка сигнатуры события (например, "Transfer(address,address,uint256)").
    • Полученный Keccak-256 используется как topic0 (идентификатор события).
  • Структуры данных и дерево состояния
    • Внутри структуры хранения Ethereum (Merkle Patricia Trie) ключи и значения проходят через Keccak-256 (иногда косвенно — через сериализацию и промежуточное кодирование).
    • Контракты часто используют keccak256 для вычисления псевдослучайных значений, индексов и ключей в хранилище.

Фактически, без Keccak-256 не работало бы ни адресное пространство, ни ABI-вызовы, ни логирование событий.

keccak256 в Solidity и EVM

В EVM используется опкод SHA3 (код 0x20), который исторически сохранил старое имя, но на самом деле выполняет именно Keccak-256.

Как это выглядит на практике:

  • Опкод SHA3 принимает в стеке указатель на память и длину.
  • Вычисляет Keccak-256 от указанного фрагмента памяти.
  • Кладёт 32-байтовый результат обратно в стек.

В Solidity поверх этого опкода реализована встроенная функция:

  • keccak256(bytes memory) — возвращает bytes32.
  • Принимает bytes (или любые типы, приводимые к bytes через ABI-кодирование, например keccak256(abi.encodePacked(...))).
  • До определённой версии Solidity функция называлась sha3, затем была переименована для честности в keccak256.

Важный нюанс:

  • keccak256(abi.encode(...)) и keccak256(abi.encodePacked(...)) используют разные схемы кодирования (см. ABI). Для function selector и некоторых паттернов хранения важно выбирать правильный вариант.

Коллизии и безопасность селекторов

Теоретически для 256-битного хеша существует огромный (но конечный) набор возможных коллизий. На практике:

  • Случайную коллизию для Keccak-256 подобрать вычислительно нереалистично на текущем уровне технологий.
  • Для function selector используются только первые 4 байта хеша (32 бита). Это значит, что теоретически можно подобрать две разные сигнатуры с одинаковым селектором.
  • В обычной жизни это не проблема (пространство большое, сигнатур — относительно мало), но при злонамеренном подборе селектора коллизии возможны.

Практические выводы:

  • Не стоит полагаться только на селектор как на уникальный идентификатор функции при анализе байткода — важно смотреть на сигнатуру.
  • В пользовательских интерфейсах и инструментах разработки (декодирование ABI) нужно иметь словари сигнатур, а не пытаться «угадывать» их по селектору.

Типичные ошибки при работе с keccak256

  • Путаница с SHA-3 в библиотеках

Использование sha3_256 вместо keccak256 в коде бекенда или скриптах миграций → несовместимые хеши, «потерянные» адреса или некорректные подписи.

  • Неверное ABI-кодирование перед хешированием

Различие между abi.encode и abi.encodePacked может приводить к коллизиям или некорректным значениям, если ожидать один формат, а использовать другой — см. ABI.

  • Псевдослучайность из keccak256 без соли

Использование keccak256(abi.encodePacked(block.timestamp)) или похожих конструкций для «рандома» во многих случаях небезопасно: майнер/валидатор и/или другие участники могут предсказать результат.

  • Разное представление строк и чисел

Строка "1" и число 1 кодируются по-разному, и keccak256 от них также различается. Важно жёстко определять формат входа.

См. также

Task Runner