技术 › 密码学
哈希:Keccak、Hs 与 Hp
门罗币如何把任意字节变成一个数或一个点
简而言之
门罗币使用 Keccak-256 哈希函数(原始 Keccak,不是标准化的 SHA3-256)。由它派生出两个函数:Hs——“哈希到标量”,以及 Hp——“哈希到点”。
Keccak-256
哈希函数接收任意数据,输出 32 个看起来随机的字节。输入改变一个比特,整个输出都会改变;从输出反推输入,或者找到两个输出相同的输入,在实践中都是不可能的。
门罗币(和以太坊一样)使用的是赢得 SHA-3 竞赛时的 Keccak——那时 NIST 还没有修改填充字节。由于这个细节,标准库里的 sha3_256() 会给出不同的结果。如果你的代码算出的结果与门罗币不一致,先检查这一点。
| 函数 | 空字符串的哈希 |
|---|---|
| Keccak-256(门罗币) | c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 |
| SHA3-256(NIST) | a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a |
在门罗币的代码中,这个函数叫做 cn_fast_hash。
Hs:哈希到标量
我们常常需要的不只是字节,而是一个标量——模 l(基点的阶)的一个数。Hs 直接完成这件事:
Hs(data) = Keccak256(data) mod l
把 32 字节的哈希按小端序读作一个数,再对 l 取模(代码中为 sc_reduce32)
Hs 的用途:
Hp:哈希到点
有时我们需要一个没有人知道其离散对数的点。不能直接用 Hs(data)·G——那样离散对数就是 Hs(data),人人都知道。Hp 通过一种特殊映射直接从哈希构造一个点(门罗币中使用 Elligator 2 的一个变体,即函数 ge_fromfe_frombytes_vartime),然后乘以余因子 8,使这个点落在正确的子群中。
Hp(data) = 8 · map_to_curve( Keccak256(data) )
Hp 的用途:
- key image:
I = x·Hp(P)——这是主要用途; - Pedersen 承诺的第二个生成元
H:H = Hp(G)(忽略编码细节)。没有人知道满足H = k·G的k——这一点至关重要,否则金额就可能被伪造。
域分离
好的做法是在哈希之前给数据加上一个标签字符串前缀("SubAddr"、"view_tag"、"amount"),这样为某个用途计算的哈希就无法被冒用于另一个用途。旧的 CryptoNote 代码很少使用标签;较新的部分(view tag、Carrot、FCMP++)则系统地使用它们。