Технический › Криптография

Хеши: Keccak, Hs и Hp

Как Monero превращает произвольные байты в число или в точку

Коротко

Monero использует хеш-функцию Keccak-256 (оригинальный Keccak, не стандартизованный SHA3-256). Из неё строятся две производные функции: Hs — «хеш в скаляр» и Hp — «хеш в точку».

Keccak-256

Хеш-функция берёт любые данные и выдаёт 32 байта, которые выглядят случайно. Изменение одного бита на входе меняет весь выход; найти вход по выходу или два входа с одинаковым выходом невозможно на практике.

Monero (как и Ethereum) использует Keccak в том виде, в каком он победил в конкурсе SHA-3, — до того, как NIST поменял байт дополнения (padding). Из-за этой мелочи sha3_256() из стандартной библиотеки даёт другой результат. Если ваш код не совпадает с Monero — первым делом проверьте это.

ФункцияХеш пустой строки
Keccak-256 (Monero)c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470
SHA3-256 (NIST)a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a

В коде Monero эта функция называется cn_fast_hash.

Hs: хеш в скаляр

Часто нужно получить из данных не просто байты, а скаляр — число по модулю l (порядка базовой точки). Hs делает это прямолинейно:

Hs(data) = Keccak256(data) mod l 32 байта хеша читаются как число little-endian и приводятся по модулю l (в коде: sc_reduce32)

Где используется Hs:

Hp: хеш в точку

Иногда нужна точка, про которую никто не знает дискретного логарифма. Нельзя просто взять Hs(data)·G — тогда логарифм равен Hs(data), и его знают все. Hp строит точку напрямую из хеша, через специальное отображение (в Monero — вариант Elligator 2, функция ge_fromfe_frombytes_vartime), а затем умножает на кофактор 8, чтобы точка лежала в нужной подгруппе.

Hp(data) = 8 · map_to_curve( Keccak256(data) )

Где используется Hp:

  • key image: I = x·Hp(P) — это главное применение;
  • второй генератор H для обязательств Педерсена: H = Hp(G) (с точностью до деталей кодирования). Никто не знает такого k, что H = k·G — и это критично для того, чтобы нельзя было подделать сумму.
Разделение доменов

Хорошая практика — добавлять к данным перед хешированием строку-метку («SubAddr», «view_tag», «amount»), чтобы хеш, вычисленный для одной цели, нельзя было подсунуть в другую. В старом коде CryptoNote меток мало, в новых частях (view tags, Carrot, FCMP++) они используются систематически.