回顾常见国密算法
常见国密算法
这次 商密杯,做点赛前的康复训练吧.
国密算法是我国商用密码体系的算法,常见的公开算法有
| 算法 | 类型 | 用途 |
|---|---|---|
| SM2 | 非对称算法 | 数字签名,公钥加密,密钥交换 |
| SM3 | 摘要算法 | 数字摘要,完整性校验 |
| SM4 | 对称分组密码 | 数据加密 |
| ZUC | 序列密码 | 移动数据通信加密 |
| SM9 | 标识密码 |
SM4分组密码算法
加密过程
输入128bit明文M=(X0,X1,X2,X3) Key=(MK0,MK1,MK2,MK3)
输出密文(X0,X1,X2,X3)
![[Pasted image 20260717193000.png]]
加密流程$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})Modn$
得到的(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)Modn$
如果t=0,验证失败
然后计算
$(x’{1},y’{1})=sG+tP_{A}$
最后计算$R=e+x’_{1}Modn$
验证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 | |
源码实现
1 | |