回顾常见国密算法
常见国密算法
这次 商密杯,做点赛前的康复训练吧. 国密算法是我国商用密码体系的算法,常见的公开算法有
| 算法 | 类型 | 用途 |
|---|---|---|
| SM2 | 非对称算法 | 数字签名,公钥加密,密钥交换 |
| SM3 | 摘要算法 | 数字摘要,完整性校验 |
| SM4 | 对称分组密码 | 数据加密 |
| ZUC | 序列密码 | 移动数据通信加密 |
| SM9 | 标识密码 |
SM4分组密码算法
加密过程
输入128bit明文M=(X0,X1,X2,X3) Key=(MK0,MK1,MK2,MK3)
输出密文(X0,X1,X2,X3)
加密流程$X_{i}$待定$X_{i+1},X_{i+2},X_{i+3}$
然后 获得 $SBOX_INPUT=X_{i+1}\oplus X_{i+2} \oplus X_{i+3} \oplus rk_{i}$
之后拆分成4*8bit进入Sbox替换,获得32bit的Sbox_output 结果
之后将这个32bit 进行上述循环左移动xxx位.得到$y_{2} ,y_{10},y,y_{18},y_{24}$
$X_{i+4}=y_{2}\oplus y_{10} \oplus y \oplus y_{18} \oplus y_{24} \oplus x_{i}$
最后获得输出密文$(X_{35},X_{34},X_{33},X_{32})$
密钥扩展
大同小异,见参考图
解密过程
$x_{4}=x_{0}\oplus T(x_{1}\oplus x_{2} \oplus x_{3 \oplus rk_{0}})$ 这是加密过程的函数 那么 $x_{0}=x_{4}\oplus T(x_{1}\oplus x_{2} \oplus x_{3 \oplus rk_{0}})$ 那么$x_{31}=x_{35}\oplus T(x_{32}\oplus x_{33} \oplus x_{34} \oplus rk_{31})$ 那么依次类推就能得到前面的
算法实现
1 | """ |
SM2算法
SM2用于签名
之前还学过这玩意,这次再看看 椭圆曲线有限域:$F_{p}={0,1,2,..p-1}$ 椭圆曲线 : $y^2=x^3+ax+b$ 已有参数
- p:有限域模数
- a,b:曲线方程参数
- $G=G(G_{x},G_{y}):基点$
- n:基点G的阶 私钥:$d_{A} \ni (n-2)$ 公钥:$P_{A}=d_{A}G=(x_{A},y_{A})$
- $ID_{A}$:用户身份标识
- $ENTL_{A}:ID_{A}的比特长度$
- 消息明文M
签名过程
生成摘要:$Z_{A}=SM_{3}(ENTL_{A}\parallel ID_{A} \parallel a \parallel b \parallel x_{G} \parallel y_{G} \parallel x_{A} \parallel y_{A})$ 之后再计算$e=SM_{3}(Z_{A} \parallel M)$ 随机生成$k\ni [1,n-1]$ k是一次性的,每次签名都不一样 计算:$(x_{1},y_{1})=[k]G$ 然后计算:$r=(e+x_{1})Mod n$ $s=(1+d_{A})^{-1}(k-rd_{A})~Mod~n$ 得到的(r,s)就是前面
验证签名
验证的过程中验证者拥有:
- 消息M
- 前面(r,s)
- A的公钥:$P_{A}$
- A的身份:$ID_{A}$
- G 首先检查: $1\le r \le n-1$ $1\le s \le n-1$ 然后重新计算$Z_{A}$和e 然后$t=(r+s)~Mod~n$ 如果t=0,验证失败 然后计算 $(x'{1},y'{1})=sG+tP_{A}$ 最后计算$R=e+x'_{1}~Mod~n$ 验证R=r即可 原理:
$k=s(1+d_{A})+r d_{A}=s+sd_{A}+rd_{A}$ 因此:$sG+tP_{A}=sG+(r+s)d_{A}G=(s+r d_{A}+s d_{A})G$ 显然成立了
SM2用于公钥加密
Alice->bob发送消息M
- 私钥为$d_{B}$
- 公钥为: $P_{B}=d_{B}G$
加密过程
Alice随机生成$k\ni [1,n-1]$ 计算$C_{1}=kG$ 之后计算$kP_{B}=kd_{B}G=(x_{2},y_{2})$ 之后再使用KDF派生出密钥流 $t=KDF(x_{2}||y_{2},klen)$,klen 是明文M的比特长度,目的是为了保证m和t的长度一样 如果t全部为0,则重新生成k 生成$C_{2}=t\oplus M$ 之后计算$C_{3}=SM_{3}(x_{2}\parallel M\parallel y_{2})$ 最终输出的密文C=(C1,C2,C3)
解密流程
解密的bob是知道$d_{B}$的 所以直接可以得到$(x_{2},y_{2})=d_{B}C_{1}$ 之后同样的利用$x_{2},y_{2}$得到t,就能计算出$M=C_{2}\oplus t$ 之后再验证C3确保没有被篡改
SM2用于密钥交换
获取各自私钥
双方长期密钥: Alice: $P_{A}=d_{A}G$ Bob: $P_{B}=d_{B}G$ 双方生成临时密钥: Alice生成临时随机数$r_{A}$ 计算:$R_{A}=r_{A}G=(x_{1},y_{1})$ Bob生成临时随机数$r_{B}$ 计算$R_{B}=r_{B}G=(x_{2},y_{2})$ 然后对临时点横坐标进行压缩处理
首先定义:
$$
w=\left\lceil\frac{\log_2 n}{2}\right\rceil-1
$$
然后分别计算 Alice 和 Bob 临时公钥横坐标的转换值:
$$
\bar{x}_1=2^w+\left(x_1\bmod 2^w\right)
$$
$$
\bar{x}_2=2^w+\left(x_2\bmod 2^w\right)
$$
其中:
-
$x_1$ 是 Alice 临时公钥 $R_A$ 的横坐标;
-
$x_2$ 是 Bob 临时公钥 $R_B$ 的横坐标;
-
$n$ 是椭圆曲线基点 $G$ 的阶;
-
$\bar{x}_1$ 和 $\bar{x}_2$ 是经过转换后得到的整数。
这里的处理并不是通常所说的“椭圆曲线点压缩”。它只是从临时公钥的横坐标中提取部分低位,并强制设置一个高位,从而得到后续密钥交换计算所需的整数。
计算共享
Alice 计算共享点
Alice 首先计算:
$$
t_A=\left(d_A+\bar{x}_1r_A\right)\bmod n
$$
然后 Alice 计算共享点:
$$
U=[h t_A]\left(P_B+[\bar{x}_2]R_B\right)
$$
设:
$$
U=(x_U,y_U)
$$
- $h$ 是椭圆曲线的余因子;表示整条椭圆曲线点的总数与基点G所生成子群的大小的倍数关系
Bob 计算共享点
Bob 首先计算:
$$
t_B=\left(d_B+\bar{x}_2r_B\right)\bmod n
$$
然后 Bob 计算共享点:
$$
V=[h t_B]\left(P_A+[\bar{x}_1]R_A\right)
$$
设:
$$
V=(x_V,y_V)
$$
当双方提供的数据正确,并且密钥交换过程没有出现异常时,应当满足:
$$
U=V
$$
也就是:
$$
x_U=x_V
$$
$$
y_U=y_V
$$
为什么双方计算出的共享点相同
Alice 的长期公钥和临时公钥分别为:
$$
P_A=[d_A]G
$$
$$
R_A=[r_A]G
$$
Bob 的长期公钥和临时公钥分别为:
$$
P_B=[d_B]G
$$
$$
R_B=[r_B]G
$$
Alice 计算:
$$
U=[h t_A]\left(P_B+[\bar{x}_2]R_B\right)
$$
$$
U=[h t_A]\left([d_B]G+[\bar{x}_2r_B]G\right)
$$
$$
U=[h t_A(d_B+\bar{x}_2r_B)]G
$$
由于:
$$
t_B=(d_B+\bar{x}_2r_B)\bmod n
$$
因此可以写为:
$$
U=[h t_At_B]G
$$
同理,Bob 计算:
$$
V=[h t_B]\left(P_A+[\bar{x}_1]R_A\right)
$$
代入:
$$
P_A=[d_A]G
$$
以及:
$$
R_A=[r_A]G
$$
得到:
$$
V=[h t_B]\left([d_A]G+[\bar{x}_1r_A]G\right)
$$
继续合并:
$$
V=[h t_B(d_A+\bar{x}_1r_A)]G
$$
由于:
$$
t_A=(d_A+\bar{x}_1r_A)\bmod n
$$
所以:
$$
V=[h t_Bt_A]G
$$
标量乘法中的整数乘法满足交换律,因此:
$$
t_At_B=t_Bt_A
$$
最终得到:
$$
U=[h t_At_B]G
$$
$$
V=[h t_Bt_A]G
$$
所以:
$$
\boxed{U=V}
$$
派生会话密钥
Alice 根据共享点 $U=(x_U,y_U)$ 计算会话密钥:
$$
K_A=
\operatorname{KDF}
\left(
x_U\parallel y_U\parallel Z_A\parallel Z_B,
klen
\right)
$$
Bob 根据共享点 $V=(x_V,y_V)$ 计算会话密钥:
$$
K_B=
\operatorname{KDF}
\left(
x_V\parallel y_V\parallel Z_A\parallel Z_B,
klen
\right)
$$
其中:
-
$Z_A$ 是 Alice 的用户杂凑值;
-
$Z_B$ 是 Bob 的用户杂凑值;
-
$klen$ 是需要派生的会话密钥长度;
-
$\parallel$ 表示字节串拼接;
-
$\operatorname{KDF}$ 表示密钥派生函数。
由于双方计算出的共享点相同:
$$
U=V
$$
因此:
$$
x_U=x_V
$$
$$
y_U=y_V
$$
双方输入 KDF 的数据完全相同,所以最终得到:
$$
\boxed{K_A=K_B}
$$
这个密钥不会直接在网络中传输,而是由 Alice 和 Bob 分别在本地计算出来。
ZUC算法
梦回一年前的西湖论剑,哎空悲切 输入是初始K 和iv 输出 Key=ZUC(K,iv),之后$C=M\oplus Key$ ZUC由这几个部分构成
密钥和初始向量装载
128bit 的密钥分别为16字节:$K=k_{0}\parallel k_{1}\parallel \dots \parallel k_{15}$ 128bit的 初始向量 分成16字节 $IV=iv_{1}\parallel \dots \parallel iv_{15}$ ZUC还自带16个固定的15bit常量 $D={d_{0},d_{1},\dots,d_{15}}$.具体见后续代码 之后我们能得到$s_{i}=k_{i}\parallel d_{i}\parallel iv_{i}$ 然后非线性函数F的输入R0和R1都清零,R0=0,R1=0
LFSR线性反馈
LFSR线性反馈有两种模式 $$ v= \left( 2^{15}S_{15} +2^{17}S_{13} +2^{21}S_{10} +2^{20}S_{4} +\left(1+2^{8}\right)S_{0} \right) \bmod \left(2^{31}-1\right) $$ 计算新的寄存器单元: $$ S_{16}=(v+u)\bmod \left(2^{31}-1\right) $$ 若: $$ S_{16}=0 $$ 则令: $$ S_{16}=2^{31}-1 $$ 随后更新线性反馈移位寄存器: $$ (S_0,S_1,\ldots,S_{15}) \leftarrow (S_1,S_2,\ldots,S_{16}) $$ 等价地,可以展开为: $$ \begin{aligned} S_0 &\leftarrow S_1,\ S_1 &\leftarrow S_2,\ S_2 &\leftarrow S_3,\ &\ \vdots\ S_{14} &\leftarrow S_{15},\ S_{15} &\leftarrow S_{16}. \end{aligned} $$ 如果寄存器从左到右排列为: $$ S_0,S_1,S_2,\ldots,S_{15} $$ 则其数据移动方向可以表示为: $$ S_0 \leftarrow S_1 \leftarrow S_2 \leftarrow \cdots \leftarrow S_{15} \leftarrow S_{16} $$ 其中,原来的 $S_0$ 被移出,新计算得到的 $S_{16}$ 从寄存器最右侧进入。
初始化模式
LFSR接收一个31bit字u的输入,对寄存器单元变量进行更新 $$ v= \left( 2^{15}S_{15} +2^{17}S_{13} +2^{21}S_{10} +2^{20}S_{4} +\left(1+2^{8}\right)S_{0} \right) \bmod \left(2^{31}-1\right) $$ 之后$S_{16}=(v+u)$,这个u来自前面的非线性函数F输出取高位次31bit 之后就 $$ S_0 \leftarrow S_1 \leftarrow S_2 \leftarrow \cdots \leftarrow S_{15} \leftarrow S_{16} $$ 其实就是变成$(s_{1},s_{2},..,s_{16})$
工作模式
没有u那部分 $$ s_{16}= \left( 2^{15}S_{15} +2^{17}S_{13} +2^{21}S_{10} +2^{20}S_{4} +\left(1+2^{8}\right)S_{0} \right) \bmod \left(2^{31}-1\right) $$ $(s_{0},..,s_{15})<--(s_{1},\dots,s_{16})$
比特重组
输入 LFSR的16bit,输出 $X_{0},X_{1},X_{2},X_{3}$ $X_{0}=S_{15}[30:15]\parallel S_{14}[15:0]$ $X_{1}=S_{11}[15:0]\parallel S_{9}[30:15]$ $X_{2}=S_{7}[15:0]\parallel S_{5}[30:15]$ $X_{3}=S_{2}[15:0]\parallel S_{0}[30:15]$
- $X_{0},X_{1},X_{2}进入非线性函数F作为输入$
- $X_{3}最后与F的输出异或,生成密钥流$
非线性函数F
据上述输入为$X_{0},X_{1},X_{2}$ 内部寄存器为$R_{0},R_{2}$ 计算流程如下 $W=X_{0}(X_{0}\oplus R_{1})\boxplus R_{2}$之后会给LFSR的初始状态 这个像田一样的表示$(a+b) Mod~2^{32}$ 同时$W_{1}=R_{1}\boxplus X_{1}$ $W_{2}=R_{2}\oplus X_{2}$ 然后W1和W2都分成两个$W_{1h},W_{1l},W_{2h},W_{2l}$ 就是高16bit和低16bit 之后开始拼接 $P=W_{1l}\parallel W_{2h}$ $Q=W_{2l}\parallel W_{1h}$ 然后定义两个线性变化$L_{1}和L_{2}$,ROTL表示循环左移 $L_{1}(X)=X\oplus ROTL_{32}(X,2)\oplus ROTL_{32}(X,10)\oplus ROTL_{32}(X,18)\oplus ROTL_{32}(X,24)$ $L_{2}(X)=X\oplus ROTL_{32}(X,8)\oplus ROTL_{32}(X,14)\oplus ROTL_{32}(X,22)\oplus ROTL_{32}(X,30)$
之后Sbox变换我就不说了,反正整体流程如下 $W=X_{0}(X_{0}\oplus R_{1})\boxplus R_{2}$ $W_{1}=R_{1}\boxplus X_{1}$ $W_{2}=R_{2}\oplus X_{2}$ $P=W_{1l}\parallel W_{2h}$ $Q=W_{2l}\parallel W_{1h}$ $U_{F}=L_{1}(P),V_{F}=L_{2}(Q)$ $R_{1}=S(U_{F}),R_{2}=S(V_{F})$ 函数会返回一个W
流密钥生成的整体流程
初始化阶段
完成密钥和iv装载后 $R_{1}=R_{2}=0$ 之后进行32轮初始化
比特重组
$(X_{0},X_{1},X_{2},X_{3})=BR(s_{0},\dots,s_{15})$
调用非线性函数F
$W=F(X_{0},X_{1},X_{2})$ 之后更新R1和R2 截取$u=W\gg{1}$
初始化模式LFSR
输入u,和s0...,s15 这样执行32轮次
初始化后丢弃部分
额外执行$BR()$ $F(X_{0},X_{1},X_{2})$ 之后再执行一次LFSR工作模式,就更新$R_{1},R_{2}$
生成密钥流
比特重组
$(X_{0},X_{1},X_{2},X_{3})=BR(S_{0},\dots,S_{15})$
调用非线性函数
$W=F(X_{0},X_{1},X_{2})$
生成密钥流字
$Z_{i}=W\oplus X_{3}$
更新lFSR
使用LFSR的工作模式更新 后面得到n个Z合并起来作为流密钥
模板
gmssl实现
1 | from gmssl import Zuc, ZUC_KEY_SIZE, ZUC_IV_SIZE |
源码实现
1 | from __future__ import annotations |