在实现sm3算法之前,根据上述章节《sm3算法基本原理》以及sm3的规范文档,可以事先定义好一些常量及通用函数备用。
先在Clion
中创建sm3.h
和sm3.c
这两个文件。
1. 常量
1.1 初始IV
初始IV,可以用宏定义来表示,在sm3.c
增加以下宏定义:
// 寄存器初始值
#define GM_SM3_IV_A 0x7380166f
#define GM_SM3_IV_B 0x4914b2b9
#define GM_SM3_IV_C 0x172442d7
#define GM_SM3_IV_D 0xda8a0600
#define GM_SM3_IV_E 0xa96f30bc
#define GM_SM3_IV_F 0x163138aa
#define GM_SM3_IV_G 0xe38dee4d
#define GM_SM3_IV_H 0xb0fb0e4e
1.2 Tj常量
在《sm3算法基本原理》的第6 -> 6.2 CF压缩函数
中有用到一个Tj常量,Tj的定义如下:
TjT_jTj:是一个常量,当0≤j≤150 \leq j \leq 150≤j≤15时,它的值是0x79cc4519,当16≤j≤6316 \leq j \leq 6316≤j≤63时,它的值是0x7a879d8a
Tj也可以用宏定义来表示,同样在sm3.c
中定义,不在头文件中定义,是因为其它地方不需要用到:
// Tj常量
#define GM_SM3_T_0 0x79CC4519
#define GM_SM3_T_1 0x7A879D8A
2. 布尔函数
sm3的规范中,涉及到两个布尔函数,分别是FFj
和GGj
。对应在规范原文在《4.3 布尔函数》
2.1 FFj函数
FFj函数的定义如下:
FFj(X,Y,Z)={X⊕Y⊕Z0≤j≤15(X∧Y)∨(X∧Z)∨(Y∧Z)16≤j≤63 FF_j(X,Y,Z)= \begin{cases} X \oplus Y \oplus Z &\quad\quad 0 \leq j \leq 15 \\ (X \wedge Y) \vee (X \wedge Z) \vee (Y \wedge Z) &\quad\quad 16 \leq j \leq 63 \end{cases} FFj(X,Y,Z)={X⊕Y⊕Z(X∧Y)∨(X∧Z)∨(Y∧Z)0≤j≤1516≤j≤63
这个公式,还是比较容易看懂,其中:
- ⊕\oplus⊕:表示异或运算
- ∧\wedge∧:表示与运算
- ∨\vee∨:表示或运算
在sm3.c
中用宏定义来表示FFj函数,实现实现如下:
// FFj函数
#define GM_SM3_FF_0(x, y, z) ( (x) ^ (y) ^ (z) )
#define GM_SM3_FF_1(x, y, z) ( ( (x) & (y) ) | ( (x) & (z) ) | ( (y) & (z) ) )
2.2 GGj函数
GGj函数的定义如下:
GGj(X,Y,Z)={X⊕Y⊕Z0≤j≤15(X∧Y)∨(¬X∧Z)16≤j≤63 GG_j(X,Y,Z)= \begin{cases} X \oplus Y \oplus Z &\quad\quad 0 \leq j \leq 15 \\ (X \wedge Y) \vee (\neg X \wedge Z) &\quad\quad 16 \leq j \leq 63 \end{cases} GGj(X,Y,Z)={X⊕Y⊕Z(X∧Y)∨(¬X∧Z)0≤j≤1516≤j≤63
与FFj函数类似,只是多了一个¬\neg¬符号,它表示的是非运算。用宏定义来表示GGj函数,实现代码如下:
// GGj函数
#define GM_SM3_GG_0(x, y, z) ( (x) ^ (y) ^ (z) )
#define GM_SM3_GG_1(x, y, z) ( ( (x) & (y) ) | ( (~(x)) & (z) ) )
3. 置换函数
sm3的规范中,涉及到两个布尔函数,分别是P0
和P1
。对应在规范原文在《4.4 置换函数》,这两个置换函数的定义如下:
P0(X)=X⊕(X⋘9)⊕(X⋘17)P1(X)=X⊕(X⋘15)⊕(X⋘23)
P_0(X)=X \oplus (X \lll 9) \oplus (X \lll 17) \\
P_1(X) = X \oplus (X \lll 15) \oplus (X \lll 23)
P0(X)=X⊕(X⋘9)⊕(X⋘17)P1(X)=X⊕(X⋘15)⊕(X⋘23)
其中⋘\lll⋘表示循环左移,所以要实现这两个置换函数,首先要实现一下循环左移,循环左移的宏定义如下:
// 循环左移
#define GM_SM3_SHL(x, n) (((x) & 0xFFFFFFFF) << (n % 32))
#define GM_SM3_ROTL(x, n) (GM_SM3_SHL((x), n) | ((x) >> (32 - (n % 32))))
3.1 P0置换函数
实现了循环左移后,再来实现P0
置换函数就不难了,实现代码如下:
// P0函数
#define GM_SM3_P_0(x) ( (x) ^ GM_SM3_ROTL( (x), 9) ^ GM_SM3_ROTL( (x), 17) )
3.2 P1置换函数
// P1函数
#define GM_SM3_P_1(x) ( (x) ^ GM_SM3_ROTL( (x), 15) ^ GM_SM3_ROTL( (x), 23) )
4. 字与字节间的转换
在sm3中,字的存储是大端模式,所以要实现字与字节间的转换,实现代码如下:
// 字节转化为字
#ifndef GM_GET_UINT32_BE
#define GM_GET_UINT32_BE(n, b, off) \
do { \
(n) = ( (uint32_t) (b)[(off) ] << 24 ) \
| ( (uint32_t) (b)[(off) + 1] << 16 ) \
| ( (uint32_t) (b)[(off) + 2] << 8 ) \
| ( (uint32_t) (b)[(off) + 3] ); \
}while(0)
#endif
// 字转化为字节
#ifndef GM_PUT_UINT32_BE
#define GM_PUT_UINT32_BE(n, b ,off) \
do { \
(b)[(off) ] = (unsigned char) ( (n) >> 24 ); \
(b)[(off) + 1] = (unsigned char) ( (n) >> 16 ); \
(b)[(off) + 2] = (unsigned char) ( (n) >> 8 ); \
(b)[(off) + 3] = (unsigned char) ( (n) ); \
}while(0)
#endif
其中参数n为字变量,参数b为字节变量,off表示字节变量b的偏移量。