文章

SM4算法简介

SM4算法简介

目录

  1. 概述
  2. 密码部件及轮函数
  3. 算法流程
  4. 密钥扩展算法
  5. 代码实现
  6. 实例

一、概述

SM4是一种分组密码算法,由我国国家密码管理局在2012年发布,常用于无线互联网加密 等领域。

项目数值
分组长度128 位
密钥长度128 位
轮数32 轮
字长32 位
轮密钥数量32 个,每个 32 位
S 盒一个固定的 8 位输入、8 位输出置换

给定:

  • 128 位明文分组 P;
  • 128 位主密钥 MK;

SM4 加密输出 128 位密文分组:

1
C = SM4_Encrypt(MK, P)

解密执行逆变换:

1
P = SM4_Decrypt(MK, C)

基本运算

SM4算法采用以下基本运算: (1) $\oplus$ 表示32 bit异或; (2) $«<i$ 表示32bit循环左移 i 位。

二、密码部件及轮函数

1.S盒

S盒是以字节为单位的非线性代换,其作用是混淆。它把一个 8 位输入映射为一个 8 位输出。查表时,高 4 位选行,低 4 位选列。

设输入字节为a,输出字节为b,则S盒的运算可表示为 \(b=S(a)\) 例如:

1
2
输入 0xEF:行 = 0xE,列 = 0xF
查表得到:S(0xEF) = 0x84

完整S盒如下:

行\列0123456789ABCDEF
0D690E9FECCE13DB716B614C228FB2C05
12B679A762ABE04C3AA44132649860699
29C4250F491EF987A33540B43EDCFAC62
3E4B31CA9C908E89580DF94FA758F3FA6
44707A7FCF37317BA83593C19E6854FA8
5686B81B27164DA8BF8EB0F4B70569D35
61E240E5E6358D1A225227C3B01217887
7D40046579FD327524C3602E7A0C4C89E
8EABF8AD240C738B5A3F7F2CEF96115A1
9E0AE5DA49B341A55AD933230F58CB1E3
A1DF6E22E8266CA60C02923AB0D534E6F
BD5DB3745DEFD8E2F03FF6A726D6C5B51
C8D1BAF92BBDDBC7F11D95C411F105AD8
D0AC13188A5CD7BBD2D74D012B8E5B4B0
E8969974A0C96777E65B9F109C56EC684
F18F07DEC3ADC4D2079EE5F3ED7CB3948

2.非线性变换 $\tau$

非线性变换 $\tau$ 是以字为单位的非线性替换,它由四个S盒组成 。

设输入为 $A=(a_{0},a_{1},a_{2},a_{3})$ ,输出为 $B=(b_{0},b_{1},b_{2},b_{3})$ 。都是4个32位的字。

\[B=(b_{0},b_{1},b_{2},b_{3})=\tau(A)=(S(a_{0}),S(a_{1}),S(a_{2}),S(a_{3}))\]

3.线性变换 L

线性变换部件L是以字为处理单位的线性变换,其输入输出都是32位的字,密码学作用为扩散。

设L的输入为字B,输出为字C,则: \(C=L(B)=B\oplus(B<<<2)\oplus(B<<<10)\oplus(B<<<18)\oplus(B<<<24)\)

4.合成变换T

合成变换T由非线性变换$\tau$和线性变换L复合而成,数据处理单位是字。设输入为字 X,则 \(T(X)=L(\tau(X))\)

5.密钥扩展线性变换 L’

密钥扩展复用同一个 S 盒,但使用不同的线性变换L’: \(C=L'(B)=B\oplus(B<<<13)\oplus(B<<<23)\)

T 和 T’ 的区别只有线性变换L与L’不同。

6.轮函数F

轮函数由上述密码部件构成。设轮函数F的输入为4个32位字($X_{0},X_{1},X_{2},X_{3}$),轮密钥是一个32位的字 rk,输出也是一个32位的字,由下式给出:

\[F(X_{0},X_{1},X_{2},X_{3},rk)=X_{0} \oplus T(X_{1}\oplus X_{2}\oplus X_{3}\oplus rk)\]

三、算法流程

1.加密算法

先把输入的16字节明文按大端序拆为4个32位字($X_{0},X_{1},X_{2},X_{3}$),输入的轮密钥为 $rk_{i}(i=0,1,\dots,31)$ 共32个字。

加密算法流程如下: \(X_{i+4}=F(X_{i} \ \ ,X_{i+1} \ \ ,X_{i+2} \ \ ,X_{i+3} \ \ ,rk_{i} )\) \(X_{i+4}=X_{i} \oplus T(X_{i+1}\oplus X_{i+2}\oplus X_{i+3}\oplus rk) \ \ (i=0,1,\dots,31)\)

为了保证和解密算法所需要的顺序一致,在加密算法后还要补一个反序处理R: \((Y_{0},Y_{1},Y_{2},Y_{3})=(X_{35},X_{34},X_{33},X_{32})=R(X_{32},X_{33},X_{34},X_{35})\)

BQACAgUAAyEGAASHRsPbAAEXxb9qYjZwzebCIBCtHvcQx7PRCA6yCwAC3iUAAmrvEVdZmXdVs353WT0E.png

2.解密算法

解密算法与加密算法相同,只是轮密钥的使用顺序相反。

这不表示每一轮函数单独就是自身的逆。可逆性来自整个字移位结构、最终反序变换以及轮密钥倒序的组合。

四、密钥扩展算法

密钥扩展算法把 128 位初始密钥生成 32 个 32 位轮密钥。

1.常数FK

在密钥扩展中使用如下的常数

参数数值
FK0A3B1BAC6
FK156AA3350
FK2677D9197
FK3B27022DC

2.固定参数CK

共使用32个固定参数 $CK_{i}$ ,每个CK是一个字

设 $ck_{i,j}$ 是 $CK_{i}$ 的第 j 字节$(i=0,1,\dots,31;j=0,1,2,3)$ 即 $CK_{i}=(ck_{i,0},\ \ ck_{i,1},\ \ ck_{i,2},\ \ ck_{i,3})$ 其中 $ck_{i,j}=(4i+j)\times7(mod \ \ 256)$

这32个固定参数如下:

iCK_iiCK_i
000070E1516C0C7CED5
11C232A3117DCE3EAF1
2383F464D18F8FF060D
3545B626919141B2229
470777E852030373E45
58C939AA1214C535A61
6A8AFB6BD22686F767D
7C4CBD2D923848B9299
8E0E7EEF524A0A7AEB5
9FC030A1125BCC3CAD1
10181F262D26D8DFE6ED
11343B424927F4FB0209
1250575E652810171E25
136C737A81292C333A41
14888F969D30484F565D
15A4ABB2B931646B7279

3.密钥扩展

先拆分输入的初始密钥为 $MK=(MK_{0},MK_{1},MK_{2},MK_{3})$ ,轮密钥扩展算法描述如下:

(1) $(K_{0},K_{1},K_{2},K_{3})=(MK_{0}\oplus FK_{0},MK_{1}\oplus FK_{1},MK_{2}\oplus FK_{2},MK_{3}\oplus FK_{3})$ (2) $For \ \ i=0,1,\dots,31 \ \ Do$ \(rk_{i}= K_{i+4}=K_{i}\oplus T'(K_{i+1}\oplus K_{i+2}\oplus K_{i+3}\oplus CK_{i})\)

BQACAgUAAyEGAASHRsPbAAEXxhhqYj9RGdemfNz-rDY2Eip1w5bNCAACQiYAAmrvEVef7GflDB_knT0E.png

五、代码实现

#define HEADER_SM4_H
#define HEADER_SM4_H
#ifdef __cplusplus
extern "C" {
#endif
typedef unsigned char muint8;
typedef unsigned long muint32;
#define LITTLE_ENDIAN   //定义小端字节序
//#define BIG_ENDIAN   //定义大端字节序
#define SM4_ENCRYPT 1   //定义加密标志
#define SM4_DECRYPT 0   //定义解密标志
#define SM4_BLOCK_LEN    16
#define SM4_KEY_LEN    16
#define SM4_IV_LEN    SM4_BLOCK_LEN
// SM4的密钥扩展算法
// 参数说明:Key为加密密钥,rk为子密钥,CryptFlag为加解密标志
int SM4KeyExt(const muint8 *Key, muint32 *rk, muint32 CryptFlag);
// SM4的加解密函数
// 参数说明:Input为输入信息分组,Output为输出分组,rk为轮密钥
int SM4Crypt(const muint8 *Input, muint8 *Output, const muint32 *rk);
//For EVP
void sm4_set_key(muint32 *rk, const unsigned char *data, int enc);
void sm4_ecb_encrypt(char *in, char *out, const muint32 *rk,int enc);
void sm4_cbc_encrypt(const unsigned char *in, unsigned char *out, long length,const muint32 *rk, unsigned char *iv, int enc);
void sm4_cfb64_encrypt(const unsigned char *in, unsigned char *out,long length, const muint32 *rk, unsigned char *ivec,
int *num, int enc);
void sm4_ofb64_encrypt(const unsigned char *in, unsigned char *out,long length, const muint32 *rk, unsigned char *ivec,int *num);
#ifdef __cplusplus
}
#endif
#endif
#include "sm4.h"
#include
//muint32 rk[32];//作为加解密上下文
const muint8 Sbox[256] = {
0xd6,0x90,0xe9,0xfe,0xcc,0xe1,0x3d,0xb7,0x16,0xb6,0x14,0xc2,0x28,0xfb,0x2c,0x05,
0x2b,0x67,0x9a,0x76,0x2a,0xbe,0x04,0xc3,0xaa,0x44,0x13,0x26,0x49,0x86,0x06,0x99,
0x9c,0x42,0x50,0xf4,0x91,0xef,0x98,0x7a,0x33,0x54,0x0b,0x43,0xed,0xcf,0xac,0x62,
0xe4,0xb3,0x1c,0xa9,0xc9,0x08,0xe8,0x95,0x80,0xdf,0x94,0xfa,0x75,0x8f,0x3f,0xa6,
0x47,0x07,0xa7,0xfc,0xf3,0x73,0x17,0xba,0x83,0x59,0x3c,0x19,0xe6,0x85,0x4f,0xa8,
0x68,0x6b,0x81,0xb2,0x71,0x64,0xda,0x8b,0xf8,0xeb,0x0f,0x4b,0x70,0x56,0x9d,0x35,
0x1e,0x24,0x0e,0x5e,0x63,0x58,0xd1,0xa2,0x25,0x22,0x7c,0x3b,0x01,0x21,0x78,0x87,
0xd4,0x00,0x46,0x57,0x9f,0xd3,0x27,0x52,0x4c,0x36,0x02,0xe7,0xa0,0xc4,0xc8,0x9e,
0xea,0xbf,0x8a,0xd2,0x40,0xc7,0x38,0xb5,0xa3,0xf7,0xf2,0xce,0xf9,0x61,0x15,0xa1,
0xe0,0xae,0x5d,0xa4,0x9b,0x34,0x1a,0x55,0xad,0x93,0x32,0x30,0xf5,0x8c,0xb1,0xe3,
0x1d,0xf6,0xe2,0x2e,0x82,0x66,0xca,0x60,0xc0,0x29,0x23,0xab,0x0d,0x53,0x4e,0x6f,
0xd5,0xdb,0x37,0x45,0xde,0xfd,0x8e,0x2f,0x03,0xff,0x6a,0x72,0x6d,0x6c,0x5b,0x51,
0x8d,0x1b,0xaf,0x92,0xbb,0xdd,0xbc,0x7f,0x11,0xd9,0x5c,0x41,0x1f,0x10,0x5a,0xd8,
0x0a,0xc1,0x31,0x88,0xa5,0xcd,0x7b,0xbd,0x2d,0x74,0xd0,0x12,0xb8,0xe5,0xb4,0xb0,
0x89,0x69,0x97,0x4a,0x0c,0x96,0x77,0x7e,0x65,0xb9,0xf1,0x09,0xc5,0x6e,0xc6,0x84,
0x18,0xf0,0x7d,0xec,0x3a,0xdc,0x4d,0x20,0x79,0xee,0x5f,0x3e,0xd7,0xcb,0x39,0x48
};
const muint32 CK[32] = {
0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279 };
#define Rotl(_x, _y) (((_x) << (_y)) | ((_x) >> (32 - (_y))))
#define ByteSub(_A) (Sbox[(_A) >> 24 & 0xFF] << 24 ^ \
Sbox[(_A) >> 16 & 0xFF] << 16 ^ \
Sbox[(_A) >> 8 & 0xFF] << 8 ^ \
Sbox[(_A) & 0xFF])
#define L1(_B) ((_B) ^ Rotl(_B, 2) ^ Rotl(_B, 10) ^ Rotl(_B, 18) ^ Rotl(_B, 24))
#define L2(_B) ((_B) ^ Rotl(_B, 13) ^ Rotl(_B, 23))
// SM4的加解密函数
// 参数说明:Input为输入信息分组,Output为输出分组,rk为轮密钥
int SM4Crypt(const muint8 *Input, muint8 *Output, const muint32 *rk)
{muint32 r, mid, x0, x1, x2, x3, *p;
p = (muint32 *)Input;
x0 = p[0];
x1 = p[1];
x2 = p[2];
x3 = p[3];
#ifdef LITTLE_ENDIAN
x0 = Rotl(x0, 16); x0 = ((x0 & 0x00FF00FF) << 8) ^ ((x0 & 0xFF00FF00) >> 8);
x1 = Rotl(x1, 16); x1 = ((x1 & 0x00FF00FF) << 8) ^ ((x1 & 0xFF00FF00) >> 8);
x2 = Rotl(x2, 16); x2 = ((x2 & 0x00FF00FF) << 8) ^ ((x2 & 0xFF00FF00) >> 8);
x3 = Rotl(x3, 16); x3 = ((x3 & 0x00FF00FF) << 8) ^ ((x3 & 0xFF00FF00) >> 8);
#endif
for (r = 0; r < 32; r += 4)
{
mid = x1 ^ x2 ^ x3 ^ rk[r + 0];
mid = ByteSub(mid);
x0 ^= L1(mid);
mid = x2 ^ x3 ^ x0 ^ rk[r + 1];
mid = ByteSub(mid);
x1 ^= L1(mid);
mid = x3 ^ x0 ^ x1 ^ rk[r + 2];
mid = ByteSub(mid);
x2 ^= L1(mid);
mid = x0 ^ x1 ^ x2 ^ rk[r + 3];
mid = ByteSub(mid);
x3 ^= L1(mid);
}
#ifdef LITTLE_ENDIAN
x0 = Rotl(x0, 16); x0 = ((x0 & 0x00FF00FF) << 8) ^ ((x0 & 0xFF00FF00) >> 8);
x1 = Rotl(x1, 16); x1 = ((x1 & 0x00FF00FF) << 8) ^ ((x1 & 0xFF00FF00) >> 8);
x2 = Rotl(x2, 16); x2 = ((x2 & 0x00FF00FF) << 8) ^ ((x2 & 0xFF00FF00) >> 8);
x3 = Rotl(x3, 16); x3 = ((x3 & 0x00FF00FF) << 8) ^ ((x3 & 0xFF00FF00) >> 8);
#endif
p = (muint32 *)Output;
p[0] = x3;
p[1] = x2;
p[2] = x1;
p[3] = x0;
return 1;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
#!/usr/bin/env python
import copy
import time
from functools import reduce

# Expanded SM4 S-boxes    Sbox table: 8bits input convert to 8 bits output
SboxTable = [
    0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05,
    0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
    0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62,
    0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, 0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6,
    0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8,
    0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, 0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35,
    0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87,
    0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52, 0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e,
    0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5, 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1,
    0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55, 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3,
    0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60, 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f,
    0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51,
    0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8,
    0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0,
    0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84,
    0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48,
]

# System parameter
FK = [0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc]

# fixed parameter
CK = [
    0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
    0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
    0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
    0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
    0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
    0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
    0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
    0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
]

ENCRYPT = 0
DECRYPT = 1


def GET_UINT32_BE(key_data):
    return int((key_data[0] << 24) | (key_data[1] << 16) | (key_data[2] << 8) | (key_data[3]))


def PUT_UINT32_BE(n):
    return [int((n >> 24) & 0xff), int((n >> 16) & 0xff), int((n >> 8) & 0xff), int((n) & 0xff)]


# rotate shift left marco definition
def SHL(x, n):
    return int(int(x << n) & 0xffffffff)


def ROTL(x, n):
    return SHL(x, n) | int((x >> (32 - n)) & 0xffffffff)


def XOR(a, b):
    return list(map(lambda x, y: x ^ y, a, b))


# look up in SboxTable and get the related value.
# args:    [in] inch: 0x00~0xFF (8 bits unsigned value).
def sm4Sbox(idx):
    return SboxTable[idx]


# Calculating round encryption key.
# args:    [in] a: a is a 32 bits unsigned value;
# return: sk[i]: i{0,1,2,3,...31}.
def sm4CalciRK(ka):
    a = PUT_UINT32_BE(ka)
    b = [sm4Sbox(i) for i in a]
    bb = GET_UINT32_BE(b)
    rk = bb ^ (ROTL(bb, 13)) ^ (ROTL(bb, 23))
    return rk


# private F(Lt) function:
# "T algorithm" == "L algorithm" + "t algorithm".
# args:    [in] a: a is a 32 bits unsigned value;
# return: c: c is calculated with line algorithm "L" and nonline algorithm "t"
def sm4Lt(ka):
    a = PUT_UINT32_BE(ka)
    b = [sm4Sbox(i) for i in a]
    bb = GET_UINT32_BE(b)
    return bb ^ (ROTL(bb, 2)) ^ (ROTL(bb, 10)) ^ (ROTL(bb, 18)) ^ (ROTL(bb, 24))


# private F function:
# Calculating and getting encryption/decryption contents.
# args:    [in] x0: original contents;
# args:    [in] x1: original contents;
# args:    [in] x2: original contents;
# args:    [in] x3: original contents;
# args:    [in] rk: encryption/decryption key;
# return the contents of encryption/decryption contents.
def sm4F(x0, x1, x2, x3, rk):
    return (x0 ^ sm4Lt(x1 ^ x2 ^ x3 ^ rk))


class Sm4(object):
    def __init__(self):
        self.sk = [0] * 32
        self.mode = ENCRYPT

    def sm4_set_key(self, key_data, mode):
        self.sm4_setkey(key_data, mode)

    def sm4_setkey(self, key, mode):
        MK = [0, 0, 0, 0]
        k = [0] * 36
        MK[0] = GET_UINT32_BE(key[0:4])
        MK[1] = GET_UINT32_BE(key[4:8])
        MK[2] = GET_UINT32_BE(key[8:12])
        MK[3] = GET_UINT32_BE(key[12:16])
        k[0:4] = XOR(MK, FK)
        for i in range(32):
            k[i + 4] = k[i] ^ (sm4CalciRK(k[i + 1] ^ k[i + 2] ^ k[i + 3] ^ CK[i]))
        self.sk = k[4:]
        self.mode = mode
        if mode == DECRYPT:
            self.sk.reverse()

    def sm4_one_round(self, sk, in_put):
        item = [GET_UINT32_BE(in_put[0:4]), GET_UINT32_BE(in_put[4:8]), GET_UINT32_BE(in_put[8:12]),
                GET_UINT32_BE(in_put[12:16])]
        res = reduce(lambda x, y: [x[1], x[2], x[3], sm4F(x[0], x[1], x[2], x[3], y)], sk, item)
        res.reverse()
        rev = map(PUT_UINT32_BE, res)
        out_put = []
        [out_put.extend(_) for _ in rev]
        return out_put

    def sm4_crypt_ecb(self, input_data):
        # SM4-ECB block encryption/decryption
        output_data = []
        tmp = [input_data[i:i + 16] for i in range(0, len(input_data), 16)]
        [output_data.extend(each) for each in map(lambda x: self.sm4_one_round(self.sk, x), tmp)]
        return output_data

    def sm4_crypt_cbc(self, iv, input_data):
        # SM4-CBC buffer encryption/decryption
        length = len(input_data)
        i = 0
        output_data = []
        tmp_input = [0] * 16
        if self.mode == ENCRYPT:
            while length > 0:
                tmp_input[0:16] = XOR(input_data[i:i + 16], iv[0:16])
                output_data += self.sm4_one_round(self.sk, tmp_input[0:16])
                iv = copy.deepcopy(output_data[i:i + 16])
                i += 16
                length -= 16
        else:
            while length > 0:
                output_data += self.sm4_one_round(self.sk, input_data[i:i + 16])
                output_data[i:i + 16] = XOR(output_data[i:i + 16], iv[0:16])
                iv = copy.deepcopy(input_data[i:i + 16])
                i += 16
                length -= 16
        return output_data


def sm4_crypt_ecb(mode, key, data):
    sm4_d = Sm4()
    sm4_d.sm4_set_key(key, mode)
    en_data = sm4_d.sm4_crypt_ecb(data)
    return en_data


def sm4_crypt_cbc(mode, key, iv, data):
    sm4_d = Sm4()
    sm4_d.sm4_set_key(key, mode)
    en_data = sm4_d.sm4_crypt_cbc(iv, data)
    return en_data


if __name__ == "__main__":
    # log_init()

    key_data = [0x5a] * 16
    input_data = [0x6b] * 128
    iv_data = [0] * 16

    sm4_d = Sm4()
    sm4_d.sm4_set_key(key_data, ENCRYPT)

    en_data = sm4_d.sm4_crypt_ecb(input_data)
    print(en_data, "en_data:")
    sm4_d.sm4_set_key(key_data, DECRYPT)
    de_data = sm4_d.sm4_crypt_ecb(en_data)
    print(de_data, "de_data:")
    if de_data == input_data:
        print("ecb check pass")
    else:
        print("ecb check fail")
        raise BaseException("error")

    sm4_d.sm4_set_key(key_data, ENCRYPT)
    en_data = sm4_d.sm4_crypt_cbc(iv_data, input_data)
    print(en_data, "en_data:")
    sm4_d.sm4_set_key(key_data, DECRYPT)
    de_data = sm4_d.sm4_crypt_cbc(iv_data, en_data)
    print(de_data, "de_data:")
    if de_data == input_data:
        print("cbc check pass")
    else:
        print("cbc check fail")
        raise BaseException("error")
    # file test
    file_path = r"test2.txt"
    ecb_path_en = r"test2k_ecb_en.txt"
    ecb_path_de = r"test2k_ecb_de.txt"
    cbc_path_en = r"test2k_cbc_en.txt"
    cbc_path_de = r"test2k_cbc_de.txt"

    key_data = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
                0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]
    iv_data = [0x5a] * 16

    with open(file_path, 'rb') as f:
        file_data = f.read()
    # file_data_list = [ord(x) for x in file_data]
    file_data_list = list(file_data)
    # 1. ECB
    sm4_d = Sm4()
    sm4_d.sm4_set_key(key_data, ENCRYPT)
    en_data = sm4_d.sm4_crypt_ecb(file_data_list)
    with open(ecb_path_en, 'w+b') as f:
        f.write(bytes(en_data))

    sm4_d.sm4_set_key(key_data, DECRYPT)
    de_data = sm4_d.sm4_crypt_ecb(en_data)
    with open(ecb_path_de, 'w+b') as f:
        f.write(bytes(de_data))
    if de_data == file_data_list:
        print("file decode pass")
    else:
        print("file decode fail")
        raise BaseException('error')

    # 2. CBC
    sm4_d.sm4_set_key(key_data, ENCRYPT)
    en_data = sm4_d.sm4_crypt_cbc(iv_data, file_data_list)
    with open(cbc_path_en, 'w+b') as f:
        f.write(bytes(en_data))

    sm4_d.sm4_set_key(key_data, DECRYPT)
    de_data = sm4_d.sm4_crypt_cbc(iv_data, en_data)
    with open(cbc_path_de, 'w+b') as f:
        f.write(bytes(de_data))
    if de_data == file_data_list:
        print("file decode pass")
    else:
        print("file decode fail")
        raise BaseException("error")
    # log_end()

六、实例

BQACAgUAAyEGAASHRsPbAAEXxjhqYkH-Zft2FSe8F79gJpyyczBDkwACaCYAAmrvEVfN6vcUGrcaST0E.png

参考

  1. (六)国密SM4算法 - 知乎
  2. 张仕斌,万武南,张金全. 应用密码学[M]. 西安: 西安电子科技大学出版社, 2017.
  3. SM4.0_百度百科
本文由作者按照 CC BY 4.0 进行授权