Hashes: Keccak, Hs and Hp
How Monero turns arbitrary bytes into a number or into a point
Monero uses the Keccak-256 hash function (original Keccak, not standardised SHA3-256). Two derived functions are built from it: Hs — "hash to scalar" and Hp — "hash to point".
Keccak-256
A hash function takes any data and outputs 32 bytes that look random. Changing one input bit changes the whole output; finding the input from the output, or two inputs with the same output, is infeasible in practice.
Monero (like Ethereum) uses Keccak as it won the SHA-3 competition — before NIST changed the padding byte. Because of that detail, sha3_256() from a standard library gives a different result. If your code doesn't match Monero, check this first.
| Function | Hash of the empty string |
|---|---|
| Keccak-256 (Monero) | c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 |
| SHA3-256 (NIST) | a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a |
In Monero's code this function is called cn_fast_hash.
Hs: hash to scalar
Often we need not just bytes but a scalar — a number modulo l (the order of the base point). Hs does it directly:
Where Hs is used:
- the view key from the spend key:
a = Hs(b); - one-time addresses:
Hs(8rA ‖ i); - subaddresses:
Hs("SubAddr\0" ‖ a ‖ i ‖ j); - challenges in signatures.
Hp: hash to point
Sometimes we need a point whose discrete logarithm nobody knows. We can't just take Hs(data)·G — then the logarithm is Hs(data) and everyone knows it. Hp builds a point directly from the hash via a special map (in Monero a variant of Elligator 2, the function ge_fromfe_frombytes_vartime), then multiplies by the cofactor 8 so the point lands in the right subgroup.
Where Hp is used:
- key images:
I = x·Hp(P)— the main use; - the second generator
Hfor Pedersen commitments:H = Hp(G)(up to encoding details). Nobody knows akwithH = k·G— and that's critical so amounts can't be forged.
Good practice is to prefix data with a label string ("SubAddr", "view_tag", "amount") before hashing, so a hash computed for one purpose can't be passed off for another. The old CryptoNote code has few labels; newer parts (view tags, Carrot, FCMP++) use them systematically.