Требуемые знания:
- 03-hash-functions
Keccak и SHA-3
Зачем это нужно: ловушка для разработчиков Ethereum
Ethereum использует Keccak-256, а не SHA-3-256. Это одно и то же? Нет! Они дают разные хеши для одних и тех же данных. Если вы используете стандартную библиотеку SHA-3 для вычисления Ethereum-адреса, вы получите неверный результат.
Давайте разберемся, почему так произошло и как устроены эти алгоритмы.
Краткая история
- 2007-2012: NIST проводит конкурс на новый стандарт хеширования (SHA-3)
- 2012: Команда Guido Bertoni, Joan Daemen, Michael Peeters, Gilles Van Assche побеждает с алгоритмом Keccak
- 2014: Ethereum начинает разработку и выбирает Keccak как хеш-функцию
- 2015 (август): NIST публикует финальный стандарт SHA-3 (FIPS 202) — но с измененным padding по сравнению с оригинальным Keccak
- 2015 (июль): Ethereum запускается с оригинальным Keccak (до публикации SHA-3)
Результат: Ethereum навсегда “застрял” с оригинальным Keccak, а стандарт SHA-3 использует другой padding.
Конструкция губки (Sponge Construction)
В отличие от SHA-256, который использует конструкцию Merkle-Damgard, Keccak/SHA-3 основан на конструкции губки (sponge construction).
Как работает губка
Состояние — это массив из 1600 бит, разделенный на две части:
- Rate (r) — открытая часть, через которую поступают данные и извлекается хеш
- Capacity (c) — скрытая часть, обеспечивающая безопасность (не доступна напрямую)
Фаза впитывания (Absorb):
- Начинаем с нулевого состояния (1600 нулевых бит)
- XOR блок входных данных с rate-частью состояния
- Применяем перестановку Keccak-f (24 раунда)
- Повторяем для каждого блока данных
Фаза выжимания (Squeeze):
- Читаем rate-часть состояния как выходные данные
- Если нужно больше выходных бит — применяем перестановку и читаем снова
- Для Keccak-256: нужно 256 бит, rate = 1088 бит — хватает одного выжимания
Сравнение с SHA-256
Преимущества конструкции губки
-
Защита от length extension attack: В SHA-256 (Merkle-Damgard) злоумышленник может продолжить хеширование, не зная исходного сообщения. С конструкцией губки это невозможно, потому что capacity-часть скрыта.
-
Гибкость выхода: Губка может производить выход произвольной длины (от 1 бита до бесконечности). SHA-256 всегда выдает ровно 256 бит.
-
Большое внутреннее состояние: 1600 бит вместо 256 — больше “скрытой” информации.
Keccak-256 vs SHA-3-256: в чем разница?
Оба алгоритма используют одну и ту же перестановку Keccak-f[1600] с одинаковыми параметрами. Разница только в padding:
- Keccak-256 (оригинальный): padding добавляет биты
10*1(бит 1, нули, бит 1) - SHA-3-256 (NIST стандарт): padding добавляет
01 || 10*1(дополнительные 2 бита domain separation)
NIST добавил domain separation для разделения SHA-3 и SHAKE (расширяемый вариант). Это минимальное изменение, но оно приводит к полностью различным хешам.
Доказательство кодом
import hashlib
from Crypto.Hash import keccak
message = b"hello"
# SHA-3-256 (стандарт NIST, FIPS 202)
sha3_hash = hashlib.sha3_256(message).hexdigest()
# Keccak-256 (Ethereum)
keccak_hash = keccak.new(data=message, digest_bits=256).hexdigest()
print(f"SHA-3-256: {sha3_hash}")
print(f"Keccak-256: {keccak_hash}")
print(f"Совпадают? {sha3_hash == keccak_hash}") # False!
Вывод:
SHA-3-256: 3338be694f50c5f338814986cdf0686453a888b84f424d792af4b9202398f392
Keccak-256: 1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8
Совпадают? False
Критически важно: При работе с Ethereum всегда используйте Keccak-256, а НЕ SHA-3-256!
Почему Ethereum использует Keccak-256
В 2014 году, когда Виталик Бутерин и команда Ethereum проектировали протокол, Keccak уже выиграл конкурс NIST, но финальный стандарт SHA-3 еще не был опубликован. Ethereum принял оригинальный Keccak.
Когда NIST опубликовал SHA-3 с измененным padding в 2015 году, Ethereum уже был запущен. Менять хеш-функцию в работающем блокчейне невозможно — это сломало бы все адреса, транзакции и смарт-контракты.
Где Ethereum использует Keccak-256
| Применение | Как используется |
|---|---|
| Адреса | address = Keccak-256(public_key)[12:] — последние 20 байт |
| Storage slots | Keccak-256(key . slot) — позиция данных в хранилище |
| Идентификаторы функций | bytes4(Keccak-256("transfer(address,uint256)")) |
| Event topics | Keccak-256(event_signature) |
| Merkle Patricia Trie | Хеширование узлов дерева состояний |
Состояние Keccak
Внутреннее состояние Keccak — это трехмерный массив бит размером 5 x 5 x 64 = 1600 бит.
Перестановка Keccak-f[1600] выполняет 24 раунда, каждый из которых состоит из 5 шагов:
| Шаг | Название | Описание |
|---|---|---|
| 1 | theta | XOR столбцов с соседями — обеспечивает диффузию |
| 2 | rho | Циклический сдвиг каждого слова на разное количество бит |
| 3 | pi | Перестановка позиций слов в матрице 5x5 |
| 4 | chi | Нелинейная операция (единственная нелинейная!) |
| 5 | iota | XOR с раундовой константой — нарушает симметрию |
Все 5 шагов вместе обеспечивают полную диффузию и нелинейность за 24 раунда.
Алгоритмический уровень
Вычисление Ethereum-адреса из публичного ключа
from Crypto.Hash import keccak
# Публичный ключ (64 байта, без префикса 0x04)
public_key_hex = "a1b2c3d4..." # 128 hex символов
# Keccak-256 от публичного ключа
public_key_bytes = bytes.fromhex(public_key_hex)
k = keccak.new(data=public_key_bytes, digest_bits=256)
hash_hex = k.hexdigest()
# Адрес = последние 20 байт (40 hex символов)
address = "0x" + hash_hex[-40:]
print(f"Ethereum address: {address}")
Идентификатор функции Solidity
from Crypto.Hash import keccak
# Сигнатура функции transfer(address,uint256)
signature = b"transfer(address,uint256)"
k = keccak.new(data=signature, digest_bits=256)
# Первые 4 байта хеша = selector
selector = k.hexdigest()[:8]
print(f"Function selector: 0x{selector}") # 0xa9059cbb
Практика
Откройте notebook 03-hashing.ipynb (раздел “Часть 3: Keccak vs SHA-3”) и:
- Убедитесь, что Keccak-256 и SHA-3-256 дают разные результаты
- Вычислите Ethereum-адрес из публичного ключа
- Вычислите селектор функции для
approve(address,uint256) - Сравните скорость SHA-256, SHA-3-256 и Keccak-256
Итоги
- Keccak/SHA-3 использует конструкцию губки вместо Merkle-Damgard
- Внутреннее состояние — 1600 бит (rate + capacity)
- Конструкция губки защищена от length extension attack
- Keccak-256 и SHA-3-256 — это разные алгоритмы (разный padding)
- Ethereum использует Keccak-256, а НЕ SHA-3-256
- Разница возникла потому, что Ethereum принял Keccak до финализации SHA-3 стандарта NIST
- При работе с Ethereum всегда используйте
Crypto.Hash.keccak, а неhashlib.sha3_256
Проверьте понимание
Закончили урок?
Отметьте его как пройденный, чтобы отслеживать свой прогресс