这篇文章上次修改于 250 天前,可能其部分内容已经发生变化,如有疑问可询问作者。
哈希函数:将任意长度的消息,映射为一个固定长(256bit)的随机数。这段随机数称为消息摘要。
摘要的长度一般不低于 256 比特。在现实世界的密码学中,算法的安全级别不能低于 128 比特。根据生日悖论,当从 $2^n$ 种可能的空间中随机生成字符串时,在已经生成大约 $2^{n/2}$ 个字符串后,发现碰撞的概率是 50%。所以如果 $2^{128}$ 就是我们想要达到安全目标所要操作的最大次数,就需要产生 256 比特的随机输出。
哈希函数关键性质:
- 单向性(抗第一原象性):已知哈希值 Y,无法在多项式时间内计算出哈希原象 X
- 弱抗碰撞性(抗第二原象性):已知(X, Y),无法在多项式时间找到 X’,使得 Y=Hash(X’)
- 强抗碰撞性(抗碰撞性):攻击者无法寻找 X, X',满足 X != X', hash(X) == hash(X)
- 压缩性:通常是将 512bit 的数据压缩为 256bit;不足 512bit 则填充
- 随机性:输出的 Y 是 256bit 的 0/1 随机字符串
- 可重复性:如果输入 x1=x2,则输出的哈希值 Y1=Y2,是函数的基本性质
MD5 和 SHA-1 缺乏抗强碰撞性的能力,已经不安全。
SHA-2 不适合计算秘密消息的哈希值,因为容易受到长度扩展攻击。
NIST 于 2007 年 12 月发表公告,征集新 SHA3 算法。 2010 年 10 月,第 2 轮遴选结束,入选 5 个算法: BLAKE/Grst1/JH/Keccak/Skein,2012 年,Keccak 最终胜出,被命名为 SHA-3。2015 年,FIPS 202 将 SHA-3 指定为新的哈希函数标准。
BLAKE2 是基于 BLAKE 实现的,是主流哈希算法中最快的,定位是安全系数最高的哈希函数。
Poseidon 哈希函数在 64bit 域上进行加法运算、乘法运算,电路门数量较低,是 zk frendly 哈希函数。
1 SHA-2
SHA-2 由 NSA 发明并在 2001 年由 NIST 进行了标准化。SHA2 在设计上借鉴了 SHA-1,有 4 个不同的版本,包括 SHA-224、SHA-256、SHA-384 和 SHA-512。应用非常广泛的是 SHA-256,可提供 128 比特的安全性,对于高度敏感的应用,应使用安全性更强的 SHA-512。
SHA-2 通过迭代调用压缩函数来计算消息的哈希。首先对输入做填充,然后划分为等长的分组,每个分组的长度等于压缩函数的输入长度。然后将压缩函数应用于消息的所有分组,每次迭代中,都将上一轮的输出作为压缩函数的第二个输入参数。
1.1 消息填充
填充的第一个比特为 1,然后补 k 个 0,直到长度满足对 512 取模后余数是 448,剩余 64bit 填充数据长度:
msg' = {msg, 1, 0, ..., 0, msg-len-64bit}
计算方法:(Len(msg) + 1 + k + 64) / 512 = 0,寻找最小自然数 k
注意,信息必须被填充,即使长度已经满足对 512 取模后余数是 448,这时要填充 512 个比特。
1.2 计算摘要
- 将消息分解成 n 个 512-bit 大小的块
- 对每个块迭代,迭代 n 次后得到 256-bit 的数字摘要
一个 256-bit 摘要的初始值为 $H_0$,与第一个数据块 $M_1$ 运算后得到 $H_1$,即完成了第一个迭代,继续与第二个数据块 $M_2$ 运算得到 $H_2$,...,依次处理,最后得到 $H_n$,即为最终的 256-bit 消息摘要:
$$ H_i = Map(H_{i-1}, M_i) $$
对迭代函数 $Map$ 便是算法的关键,包含了线性变换、非线性变换和轮常量加。
迭代可形象表示为:
图中 256-bit 的 $H_i$ 被描述 8 个小块,这是因为 SHA256 算法中的最小运算单元称为”字” (Word),一个字是 32 位。
第一次迭代中,$H_0$ 的来源:对自然数中前 8 个质数(2, 3, 5, 7, 11, 13, 17, 19)的平方根的小数部分取前 32bit
$$ \begin{align} &H_0[0] := 0x6a09e667 \\ &H_0[1] := 0xbb67ae85 \\ &H_0[2] := 0x3c6ef372 \\ &H_0[3] := 0xa54ff53a \\ &H_0[4] := 0x510e527f \\ &H_0[5] := 0x9b05688c \\ &H_0[6] := 0x1f83d9ab \\ &H_0[7] := 0x5be0cd19 \\ \end{align} $$
例如:$ \sqrt{2} $ 小数部分约为 $0.414213562373095048≈6∗16^{−1}+a∗16^{−2}+0∗16^{−3}+...$,质数 2 的平方根的小数部分取前 32bit 就得到 0x6a09e667
Map 映射函数
也就是对称加密中的轮函数 Round Function
Step1 扩展函数
输入:512bits(16个字),输出:2048bits(64个字)
- 前 16 个字直接从输入获得:512bit = $W_0, W_1, ..., W_{15}$
剩余 48 个字由迭代公式得到:$W_t = \sigma_1(W_{t-2}) + \sigma_0(W_{t-15}) + W_{t-16}$
其中
$$ \begin{align} &\sigma_0(x) = S^7(x) \bigoplus S^{18}(x) \bigoplus R^3(x) \\ &\sigma_1(x) = S^{17}(x) \bigoplus S^{19}(x) \bigoplus R^{10}(x) \end{align} $$
其中,$\bigoplus$ 表示按位异或,$S^n$ 表示循环右移 n 个 bit,$R^n$ 表示右移 n 个 bit
Step2 压缩函数
进行 64 次迭代,每次迭代的输入为 64 个常量 $K$ 和 step1 扩展出的 64 个字 $W$,每次迭代如下图所示:
64 个常量 $K$ 来源:对自然数中前 64 个质 数 (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97…) 的立方根的小数部分取前 32 bit
上图中涉及到运算有:
$$ \begin{align} & Ch(x, y, z) = (x ∧ y) \bigoplus (¬x ∧ z) \\ & Ma(x, y, z) = (x ∧ y) \bigoplus (x ∧ z) \bigoplus (y ∧ z) \\ & Σ0 = S^2(x) \bigoplus S^{13}(x) \bigoplus S^{22}(x) \\ & Σ1 = S^6(x) \bigoplus S^{11}(x) \bigoplus S^{25}(x) \\ \end{align} $$
红色的田字方块为:相加后 mod $2^{32}$,其中一个红色方框是字与常量的模加 $W_t + K_t$ (mod $2^{32}$)
ABCDEFGH 的初始值为 $H_0$
2 BLAKE2
BLAKE 由瑞士的 Aumasson 等人设计,采用了 HAIFA 算法迭代结构,其中的压缩算法基于 ChaCha 流密码,内部结构采用 Davies-Meyer 模式,在压缩函数中,采用模余 322 加法运算与 XOR 运算,实现计算的非线性。
BLAKE2 基于 BLAKE 进行了改进,速度和安全性都有所提升。
BLAKE2b 是为 64 为平台设计的,输出 512 bits;BLAKE2s 是为 32 位平台设计的,输出 256 bits。
2.1 消息填充
如果数据库的长度是数据块的整数倍,则最后一个数据块不需要填充,不需要像 BLAKE、MD5 和 SHA-2 那样需要填充数据的长度。
2.2 计算摘要
压缩函数 $F_n(h, m, t, f) = h'$ 输入包括 4 部分:链值 h,消息块 m,计数器 t 和终结标志 f,其中,$h=h_0||...||h_7$,$m=m_0||...||m_{15}$,$t=t_0||t_1$,$f=f_0||f_1$
$f_0, f_1$ 是为了避免如 exploit fixed points 等弱点引入的,作为压缩函数的辅助输入
- 填充的安全性被转移到终结标志 $f_0$,如果是最后一个数据块,则 $f_0 = \left\{FF...FF\right\}$,否则 $f_0 = \left\{00...00\right\}$
- 终结标志 $f_1$ 用来在树散列模式中标识最后一个节点,如果是最后一个节点,则 $f_1 = \left\{FF...FF\right\}$,否则 $f_1 = \left\{00...00\right\}$
压缩函数 $F_n$ 包含 3 个步骤:initialization,轮函数 $G$ 和 finalization,包括线性变换、非线性变换、轮常量加
- Initialization(h, t, f, IV) = v
$$ v = \begin{bmatrix} v_0 & v_1 & v_2 & v_3 \\ v_4 & v_5 & v_6 & v_7 \\ v_8 & v_9 & v_{10} & v_{11} \\ v_{12} & v_{13} & v_{14} & v_{15} \end{bmatrix} = \begin{bmatrix} h_0 & h_1 & h_2 & h_3 \\ h_4 & h_5 & h_6 & h_7 \\ IV_0 & IV_1 & IV_2 & IV_3 \\ t_0 \bigoplus IV_4 & t_1 \bigoplus IV_5 & f_0 \bigoplus IV_6 & f_1 \bigoplus IV_7\end{bmatrix} $$
其中,
$$ \begin{align} &IV_0 = 0x6a09e667f3bcc908 \space // \space Frac(sqrt(2)) \\ &IV_1 = 0xbb67ae8584caa73b \space // \space Frac(sqrt(3))\\ &IV_2 = 0x3c6ef372fe94f82b \space // \space Frac(sqrt(5))\\ &IV_3 = 0xa54ff53a5f1d36f1 \space // \space Frac(sqrt(7))\\ &IV_4 = 0x510e527fade682d1 \space // \space Frac(sqrt(11))\\ &IV_5 = 0x9b05688c2b3e6c1f \space // \space Frac(sqrt(13))\\ &IV_6 = 0x1f83d9abfb41bd6b \space // \space Frac(sqrt(17))\\ &IV_7 = 0x5be0cd19137e2179 \space // \space Frac(sqrt(19))\\ \end{align} $$
轮函数 $G(m, v) = v'$
8 个轮函数 $G_0, ..., G_7$ 执行 10 轮
输入 512 bit,输出 512 bit
$G_i$ 的输入和输出可形象地用下图表示:
$$ \begin{align} &[v_0, v_4, v_8, v_{12}] = G_0(v_0, v_4, v_8, v_{12}, m_{\sigma_0}, m_{\sigma_1}) \\ &[v_1, v_5, v_9, v_{13}]=G_1(v_1, v_5, v_9, v_{13}, m_{\sigma_2}, m_{\sigma_3})\\ &[v_2, v_6, v_{10}, v_{14}]=G_2(v_2, v_6, v_{10}, v_{14}, m_{\sigma_4}, m_{\sigma_5})\\ &[v_3, v_7, v_{11}, v_{15}]=G_3(v_3, v_7, v_{11}, v_{15}, m_{\sigma_6}, m_{\sigma_7}) \\ &[v_0, v_5, v_{10}, v_{15}]=G_4(v_0, v_5, v_{10}, v_{15}, m_{\sigma_8}, m_{\sigma_9})\\ &[v_1, v_6, v_{11}, v_{12}]=G_5(v_1, v_6, v_{11}, v_{12}, m_{\sigma_{10}}, m_{\sigma_{11}})\\ &[v_3, v_7, v_8, v_{13}]=G_6(v_3, v_7, v_8, v_{13}, m_{\sigma_{12}}, m_{\sigma_{13}})\\ &[v_3, v_4, v_9, v_{14}]=G_7(v_3, v_4, v_9, v_{14}, m_{\sigma_{14}}, m_{\sigma_{15}}) \\ \end{align} $$
其中,$\sigma$ 是一个置换矩阵。轮函数 G 将两个输入 $x, y$ 混合为向量 $v$ 中的 4 个字,下标为 $a, b, c ,d$,如下所示:
FUNCTION G( v[0..15], a, b, c, d, x, y ) | v[a] := (v[a] + v[b] + x) mod 2**w | v[d] := (v[d] ^ v[a]) >>> R1 // R1: blake2b 是 32,blake2s 是 16 | v[c] := (v[c] + v[d]) mod 2**w | v[b] := (v[b] ^ v[c]) >>> R2 // R2: blake2b 是 24,blake2s 是 12 | v[a] := (v[a] + v[b] + y) mod 2**w | v[d] := (v[d] ^ v[a]) >>> R3 // R3: blake2b 是 16,blake2s 是 8 | v[c] := (v[c] + v[d]) mod 2**w | v[b] := (v[b] ^ v[c]) >>> R4 // R4: blake2b 是 63,blake2s 是 7 | RETURN v[0..15] END FUNCTION.
Finalization$(h, v') = h'$ 压缩输出结果
$$ \begin{align} &h'_0 = h_0 \bigoplus v'_0 \bigoplus v'_8 \\ &h'_1 = h_1 \bigoplus v'_1 \bigoplus v'_9 \\ &h'_2 = h_2 \bigoplus v'_2 \bigoplus v'_{10} \\ &h'_3 = h_3 \bigoplus v'_3 \bigoplus v'_{10} \\ &h'_4 = h_4 \bigoplus v'_4 \bigoplus v'_{12} \\ &h'_5 = h_5 \bigoplus v'_5 \bigoplus v'_{13} \\ &h'_6 = h_6 \bigoplus v'_6 \bigoplus v'_{14} \\ &h'_7 = h_7 \bigoplus v'_7 \bigoplus v'_{15} \\ \end{align} $$
3 SHA3-Keccak
采用海绵结构(sponge construction),在预处理(padding 并分成大小相同的块)后,海绵结构主要分成两个阶段:
- 吸入阶段(absorbing phase):将块 $x_i$ 传入算法并处理。
- 挤出阶段(squeezing phase):产生固定长度的输出。
这两个阶段使用同一个轮函数 Keccak-f。下图以 8 bit 置换为例,Keccak-f 将大小为 8 bit 的输入随机化为长度相同的输出。输入和输出都分为两部分:比率(长度为 r)和容量(长度为 c)。容量也看作秘密值,容量越大,海绵结构就越安全。
- r:比特率(bit rate),为每个输入块的长度
- c:容量(capacity),为输出长度的两倍
- b:向量的长度,b = r + c,b 的值依赖于指数 L,即 L = 6, b = $25 * (2^L)$ = 1600
- Keccak-256:b = 1600, r = 1088, c = 512,输出长度为 256,安全级别为 128
为了吸收哈希函数输入的 00101 这 5 个比特,可以使用比率为 5 比特的海绵结构将 5 比特的输入与比率比特(初始化为 0)进行异或,然后将产生的结果作为置换函数的输入,实现对输入的随机化:
如果想计算更长输入的哈希值,需要
(1)对输入进行填充(如有必要),并将输入分成与比率比特串长度相等的分组。
填充规则:m' = {m, 1000001},0 的个数 k 为满足 (len(msg) + 2 + k) / r = 0 的最小自然数
(2)迭代调用置换函数,每次将消息分组与置换输入的比率比特串进行异或,然后将中间输出状态输入置换函数,直至处理完所有的消息分组为止:
为了产生一个摘要,可以简单地截取海绵结构的最后输出状态的比率比特串(不会使用容量比特串)。为了获得更长的摘要,可以继续读取输出状态的比率比特串部分,然后对其进行置换:
3.1 轮函数
输入为 b = r + c 比特,输出为 b = r + c
轮函数 Kecaak-f 内部有 5 个变换,前 3 个是线性变换,第 4 个是非线性变换,最后一个是轮常量/轮密钥加
Kecaak-f 包含 n 轮,n 的取值与之前计算 b 时用到的指数 L 成线性关系,对于 Keccak-256,有:
$$ L = log_2W=log_264=6 \\ n=12 + 2L = 24 $$
在每一轮中,执行 θ(theta)、ρ(rho)、π(pi)、χ(chi)、ι(iota)。将 b = 1600 比特数据排列成一个 5 5 64 的立体。
- AES 加密将数据排为平面矩阵,轮函数包含 4 种操作:字节替代(S 盒子)、行移位、列混淆和轮密钥加。
- SHA3-Keccak 将数据排为立体,轮函数包括 5 个子函数:θ(theta)、ρ(rho)、π(pi)、 χ(chi)、ι(iota)。
3.1.1 Theta (θ) 函数
二进制的加法运算就是二进制异或
列与周围 2 列元素相加: $a'[x][y][z] = a[x][y][z] + \sum^4_{y'=0}a[x-1][y'][z] + \sum^4_{y'=0}a[x+1][y'][z-1]$
3.1.2 Rho (ρ) 和 Pi (π) 函数
Rho (ρ) 是在深度方向循环移位:$a'[x][y][z]=a[x][y][z-(t+1)(t+2)/2], t \in [0, ..., 24], (x,y)=(1,0) \begin{pmatrix} 0 & 2 \\ 1 & 3\end{pmatrix}^t$
Pi (π) 是在平面的旋转:$\begin{pmatrix} x'\\y'\end{pmatrix}=\begin{pmatrix} 0 & 1 \\ 2 & 3\end{pmatrix}^t\begin{pmatrix} x\\y\end{pmatrix}, t=0,...,23$
Rho (ρ) 和 Pi (π) 结合成一个式子,相当于深度方向的移位和平面的旋转同时进行。
3.1.3 Chi (χ) 函数
唯一的非线性变换,将某一行的数据与后面两行数据累加
For x=0,…,4 {
For y=0,…,4 {
a’[x][y]= a[x][y]⊕(NOT a[x+1][y]) AND a[x+2][y]
}
}
3.1.4 iota (ι) 函数
异或轮函数,消除对称性:$a’[0][0]= a[0][0] ⊕ RC, z=0,…,23$
其中,RC 是常量,值与轮数有关
4 Poseidon 哈希函数
Poseidon 哈希函数在 64bit 域上进行加法运算、乘法运算,电路门数量较低,是 zk frendly 哈希函数。
初始向量:$I=0^r||0^c$,r-bit 的 0 和 c-bit 的 0
将数据分为多个块,例如 4 块 $m=\left\{m_1 || m_2 || m_3 || m_4\right\}$,输出 $h=\left\{h_1 || h_2\right\}$
Poseidon 的轮函数包括:
- 轮密钥/常量加(添加常量)
- 非线性变换(S 盒子) $S-box( x ) = x^5$
- 线性变换(MDS 矩阵/满秩) $\vec{m'} = A \cdot \vec{m}$
参考
戴维-王. 深入浅出密码学[M]
BLAKE2: simpler, smaller, fast as MD5
没有评论