SM4国密算法原理及分析
zsk Lv4

image

明文:31323334353637383930616263646566(16个字节)
KEY:31323334353637383930616263646566(16个字节)

加密方式

ecb模式:它将明文消息分成固定长度的块,然后独立地对每个块进行加密。将所有密文块连接起来,形成最终的密文。
cbc模式:通过将前一个密文块与当前明文块异或后再加密来增强安全性。第一组与iv异或加密后的值参与第二组异或。将所有密文块连接起来,形成最终的密文。

密钥编排

固定参数CK

1
2
3
4
5
6
7
8
9
10
SM4_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
]

S盒

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
SM4_BOX = [
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,
]

对密钥进行四字节分割

1
2
3
4
MK0: 31323334
MK1: 35363738
MK2: 39306162
MK3: 63646566

分别与FK进行异或

1
SM4_FK = [0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc]

异或结果k

1
2
3
4
k0: 928389F2
k1: 639C0468
k2: 5E4DF0F5
k3: D11447BA

密钥编排共32轮 生成 4 * 32 个字节的数组
第一轮 当 i = 0 时
box_in = k1 ^ k2^ k3^ SM4_CK[i]
即 box_in = 0x639C0468 ^ 0x5E4DF0F5 ^ 0xD11447BA ^ 0x70E15 = 0xECC2BD32

将 box_in 0xECC2BD32 拆分成四个单字节进行S盒的替换
box_out = SM4_BOX[0xEC] SM4_BOX[0xC2] SM4_BOX[0xBD] SM4_BOX[0x32]
即 box_out = C5 AF 6C 1C

得到轮密钥
key_r[i] = k0 ^ box_out ^ box_out左移13位 ^ box_out左移23位
即 key_r[0] = 0x928389F2 ^ 0xC5AF6C1C ^ 0xED8398B5 ^ 0xE62D7B6 = 0xB4CDAAED

最后将k1赋值k0,k2赋值k1,k3赋值k2,key_r[i]赋值k3
即 k0 = k1 = 0x639C0468
k1 = k2 = 0x5E4DF0F5
k2 = k3 = 0xD11447BA
k3 = key_r[0] = 0xB4CDAAED

循环得到32个key_r

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
key_r = ['0xB4CDAAED',
'0x393A0943',
'0x5419A218',
'0x430FEB9A',
'0xB7B64328',
'0x89FD60C4',
'0x80FED71C',
'0x78CC97DB',
'0x541BB50E',
'0x9E47804C',
'0xE4558316',
'0xAF89E123',
'0xDA4E6F12',
'0x1A123F42',
'0x163D31AC',
'0x41782FDF',
'0xAD1F9760',
'0x22F3ED65',
'0xD6A523CF',
'0x5D5258D6',
'0x6964712A',
'0x81C9EA81',
'0xC78DF2DB',
'0xCDFB8685',
'0x4860C851',
'0xC6EEAC9F',
'0xEB2F69F3',
'0x679AB10F',
'0x8383A684',
'0xE38ED125',
'0x894B0AEA',
'0x68C6DFA8']

密钥编排代码逻辑

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
 SM4_FK = [0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc]

# CK
SM4_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
]
# S盒
SM4_BOX = [
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,
]


class SM4(object):
def __init__(self):
self.key_r = [0] * 32

# 拼接四个单字节
@staticmethod
def get_uint32_be(key_data):
return (key_data[0] << 24) | (key_data[1] << 16) | (key_data[2] << 8) | (key_data[3])

# 将四个字节拆分
@staticmethod
def put_uint32_be(src):
return [(src >> 24) & 0xFF, (src >> 16) & 0xFF, (src >> 8) & 0xFF, src & 0xFF]

# 循环左移
@staticmethod
def rotl(x, n):
return ((x << n) & 0xffffffff) | ((x >> (32 - n)) & 0xffffffff)

def _round_key(self, ka):
b = [0, 0, 0, 0]
a = self.put_uint32_be(ka)
b[0] = SM4_BOX[a[0]]
b[1] = SM4_BOX[a[1]]
b[2] = SM4_BOX[a[2]]
b[3] = SM4_BOX[a[3]]
box_out = self.get_uint32_be(b[0:4])
rk = box_out ^ (self.rotl(box_out, 13)) ^ (self.rotl(box_out, 23))
return rk

def key_generate(self, key):
key = self.bytes_to_list(key)
k = [0, 0, 0, 0]
k0 = self.get_uint32_be(key[0:4])
k1 = self.get_uint32_be(key[4:8])
k2 = self.get_uint32_be(key[8:12])
k3 = self.get_uint32_be(key[12:16])

k[0] = k0 ^ SM4_FK[0]
k[1] = k1 ^ SM4_FK[1]
k[2] = k2 ^ SM4_FK[2]
k[3] = k3 ^ SM4_FK[3]

for i in range(32):
box_in = k[1] ^ k[2] ^ k[3] ^ SM4_CK[i]
self.key_r[i] = k[0] ^ self._round_key(box_in)
k[0] = k[1]
k[1] = k[2]
k[2] = k[3]
k[3] = self.key_r[i]

明文处理

明文不满16字节及16字节倍数,需要填充至16字节的倍数,如果已经是16字节倍数则填充一组16字节
两种填充规则:

  • PKCS7: 在明文末尾添加填充字节,填充字节的值等于填充的字节数。
    例如明文是 31323334353637383930 长度为10字节,则在末尾添加6个值为0x06的字节,填充值16字节。
  • ZERO: 在明文末尾添加填充0x0,填充字节的值等于填充的字节数。
    例如明文是 31323334353637383930 长度为10字节,则在末尾添加6个值为0x00的字节,填充值16字节。

填充如果明文大于16字节,需要分组处理,每16字节一组
对明文 31323334353637383930616263646566 进行四字节分割

1
2
3
4
x0: 31323334
x1: 35363738
x2: 39306162
x3: 63646566

明文处理共32轮 最终的 x3 x2 x1 x0 组成16字节密文
第一轮 当 i = 0 时
box_input = x1 ^ x2 ^ x3 ^ key_r[i]
即 box_input = 0x35363738 ^ 0x39306162 ^ 0x63646566 ^ 0xB4CDAAED = 0xDBAF99D1

将 box_input 0xDBAF99D1 拆分成四个单字节进行S盒的替换
box_out = SM4_BOX[0xDB] SM4_BOX[0xAF] SM4_BOX[0x99] SM4_BOX[0xD1]
即 box_out = 12 6F 93 C1

box_out 循环左移与明文异或
temp = x0 ^ box_out ^ box_out左移2位 ^ box_out左移10位 ^ box_out左移18位 ^ box_out左移24位
即 temp = 0x31323334 ^ 0x126F93C1 ^ 0x49BE4F04 ^ 0xBE4F0449 ^ 0x4F0449BE ^ 0xC1126F93 = 0x5ABACD95

最后将x1赋值x0,x2赋值x1,x3赋值x2,temp赋值x3
即 x0 = x1 = 0x35363738
x1 = x2 = 0x39306162
x2 = x3 = 0x63646566
x3 = temp = 0x5ABACD95

循环得到32轮后结果
x0 = 0x6E15AEA9
x1 = 0x1B3702C5
x2 = 0xE3A63336
x3 = 0x071F23E0

最终结果 x3 x2 x1 x0
output = x3 x2 x1 x0
即 out_put = 071F23E0 E3A63336 1B3702C5 6E15AEA9

加密逻辑

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
PKCS7 = 0
ZERO = 1

class SM4(object):
def __init__(self, padding_mode=PKCS7):
self.key_r = [0] * 32
self.padding_mode = padding_mode

# 拼接四个单字节
@staticmethod
def get_uint32_be(key_data):
return (key_data[0] << 24) | (key_data[1] << 16) | (key_data[2] << 8) | (key_data[3])

# 将四个字节拆分
@staticmethod
def put_uint32_be(src):
return [(src >> 24) & 0xFF, (src >> 16) & 0xFF, (src >> 8) & 0xFF, src & 0xFF]

# 循环左移
@staticmethod
def rotl(x, n):
return ((x << n) & 0xffffffff) | ((x >> (32 - n)) & 0xffffffff)

# 将列表转换为bytes
@staticmethod
def list_to_bytes(data):
return b''.join([bytes((i,)) for i in data])

# 将bytes转换为列表
@staticmethod
def bytes_to_list(data):
return [i for i in data]

# 明文填充
@staticmethod
def pkcs7_padding(data, block=16):
return data + [(16 - len(data) % block) for _ in range(16 - len(data) % block)]

@staticmethod
def zero_padding(data, block=16):
return data + [0 for _ in range(16 - len(data) % block)]

def one_round(self, in_put):
in_put = self.bytes_to_list(in_put)
x0 = self.get_uint32_be(in_put[0:4])
x1 = self.get_uint32_be(in_put[4:8])
x2 = self.get_uint32_be(in_put[8:12])
x3 = self.get_uint32_be(in_put[12:16])
for i in range(32):
box_input = x1 ^ x2 ^ x3 ^ self.key_r[i]
a = self.put_uint32_be(box_input)
b = [0, 0, 0, 0]
b[0] = SM4_BOX[a[0]]
b[1] = SM4_BOX[a[1]]
b[2] = SM4_BOX[a[2]]
b[3] = SM4_BOX[a[3]]
box_out = self.get_uint32_be(b[0:4])
temp = x0 ^ box_out ^ self.rotl(box_out, 2) ^ self.rotl(box_out, 10) ^ self.rotl(box_out, 18) ^ self.rotl(box_out, 24)
x0, x1, x2, x3 = x1, x2, x3, temp
return self.put_uint32_be(x3) + self.put_uint32_be(x2) + self.put_uint32_be(x1) + self.put_uint32_be(x0)

def encrypt_ecb(self, input_data):
input_data = self.bytes_to_list(input_data)
if self.padding_mode == PKCS7:
input_data = self.pkcs7_padding(input_data)
elif self.padding_mode == ZERO:
input_data = self.zero_padding(input_data)

length = len(input_data)
i = 0
output_data = []
while length > 0:
output_data += self.one_round(input_data[i:i + 16])
i += 16
length -= 16
return self.list_to_bytes(output_data)

SM4 CBC模式

上面实现的是ecb模式,cbc模式会在分组加密的时候与iv异或,异或后的结果替换明文参与加密。且每组加密完的结果会做为下一轮的iv参与异或。
image

明文:31323334353637383930616263646566(16个字节)
KEY:31323334353637383930616263646566(16个字节)
IV: 1234567890abcdef1234567890abcdef(16个字节)

明文和IV每个字节分别异或后拼接
明文: 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38 0x39 0x30 0x61 0x62 0x63 0x64 0x65 0x66
IV: 0x12 0x34 0x56 0x78 0x90 0xab 0xcd 0xef 0x12 0x34 0x56 0x78 0x90 0xab 0xcd 0xef
结果: 23 06 65 4c a5 9d fa d7 2b 04 37 1a f3 cf a8 89
异或后的结果当成“明文”参与加密,其他流程跟ecb模式一样

代码逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def encrypt_cbc(self, iv, input_data):
input_data = self.bytes_to_list(input_data)
iv = self.bytes_to_list(iv)
if self.padding_mode == PKCS7:
input_data = self.pkcs7_padding(input_data)
elif self.padding_mode == ZERO:
input_data = self.zero_padding(input_data)

length = len(input_data)
i = 0
output_data = []
tmp_input = [0] * 16
while length > 0:
tmp_input[0:16] = self.xor(input_data[i:i + 16], iv[0:16])
output_data += self.one_round(tmp_input[0:16])
iv = copy.deepcopy(output_data[i:i + 16])
i += 16
length -= 16
return self.list_to_bytes(output_data)

完整代码

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
import copy

SM4_FK = [0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc]

# CK
SM4_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
]
# S盒
SM4_BOX = [
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,
]

PKCS7 = 0
ZERO = 1

class SM4(object):
def __init__(self, padding_mode=PKCS7):
self.key_r = [0] * 32
self.padding_mode = padding_mode

# 拼接四个单字节
@staticmethod
def get_uint32_be(key_data):
return (key_data[0] << 24) | (key_data[1] << 16) | (key_data[2] << 8) | (key_data[3])

# 将四个字节拆分
@staticmethod
def put_uint32_be(src):
return [(src >> 24) & 0xFF, (src >> 16) & 0xFF, (src >> 8) & 0xFF, src & 0xFF]

# 循环左移
@staticmethod
def rotl(x, n):
return ((x << n) & 0xffffffff) | ((x >> (32 - n)) & 0xffffffff)

# 将列表转换为bytes
@staticmethod
def list_to_bytes(data):
return b''.join([bytes((i,)) for i in data])

# 将bytes转换为列表
@staticmethod
def bytes_to_list(data):
return [i for i in data]

# 明文填充
@staticmethod
def pkcs7_padding(data, block=16):
return data + [(16 - len(data) % block) for _ in range(16 - len(data) % block)]

@staticmethod
def zero_padding(data, block=16):
return data + [0 for _ in range(16 - len(data) % block)]

@staticmethod
def xor(a, b):
return list(map(lambda x, y: x ^ y, a, b))

def _round_key(self, ka):
b = [0, 0, 0, 0]
a = self.put_uint32_be(ka)
b[0] = SM4_BOX[a[0]]
b[1] = SM4_BOX[a[1]]
b[2] = SM4_BOX[a[2]]
b[3] = SM4_BOX[a[3]]
box_out = self.get_uint32_be(b[0:4])
rk = box_out ^ (self.rotl(box_out, 13)) ^ (self.rotl(box_out, 23))
return rk

def key_generate(self, key):
key = self.bytes_to_list(key)
k = [0, 0, 0, 0]
k0 = self.get_uint32_be(key[0:4])
k1 = self.get_uint32_be(key[4:8])
k2 = self.get_uint32_be(key[8:12])
k3 = self.get_uint32_be(key[12:16])

k[0] = k0 ^ SM4_FK[0]
k[1] = k1 ^ SM4_FK[1]
k[2] = k2 ^ SM4_FK[2]
k[3] = k3 ^ SM4_FK[3]

for i in range(32):
box_in = k[1] ^ k[2] ^ k[3] ^ SM4_CK[i]
self.key_r[i] = k[0] ^ self._round_key(box_in)
k[0] = k[1]
k[1] = k[2]
k[2] = k[3]
k[3] = self.key_r[i]

def one_round(self, in_put):
in_put = self.bytes_to_list(in_put)
x0 = self.get_uint32_be(in_put[0:4])
x1 = self.get_uint32_be(in_put[4:8])
x2 = self.get_uint32_be(in_put[8:12])
x3 = self.get_uint32_be(in_put[12:16])
for i in range(32):
box_input = x1 ^ x2 ^ x3 ^ self.key_r[i]
a = self.put_uint32_be(box_input)
b = [0, 0, 0, 0]
b[0] = SM4_BOX[a[0]]
b[1] = SM4_BOX[a[1]]
b[2] = SM4_BOX[a[2]]
b[3] = SM4_BOX[a[3]]
box_out = self.get_uint32_be(b[0:4])
temp = x0 ^ box_out ^ self.rotl(box_out, 2) ^ self.rotl(box_out, 10) ^ self.rotl(box_out, 18) ^ self.rotl(box_out, 24)
x0, x1, x2, x3 = x1, x2, x3, temp
return self.put_uint32_be(x3) + self.put_uint32_be(x2) + self.put_uint32_be(x1) + self.put_uint32_be(x0)

def encrypt_ecb(self, input_data):
input_data = self.bytes_to_list(input_data)
if self.padding_mode == PKCS7:
input_data = self.pkcs7_padding(input_data)
elif self.padding_mode == ZERO:
input_data = self.zero_padding(input_data)

length = len(input_data)
i = 0
output_data = []
while length > 0:
output_data += self.one_round(input_data[i:i + 16])
i += 16
length -= 16
return self.list_to_bytes(output_data)

def encrypt_cbc(self, iv, input_data):
input_data = self.bytes_to_list(input_data)
iv = self.bytes_to_list(iv)
if self.padding_mode == PKCS7:
input_data = self.pkcs7_padding(input_data)
elif self.padding_mode == ZERO:
input_data = self.zero_padding(input_data)

length = len(input_data)
i = 0
output_data = []
tmp_input = [0] * 16
while length > 0:
tmp_input[0:16] = self.xor(input_data[i:i + 16], iv[0:16])
output_data += self.one_round(tmp_input[0:16])
iv = copy.deepcopy(output_data[i:i + 16])
i += 16
length -= 16
return self.list_to_bytes(output_data)

if __name__ == '__main__':
# 16进制密钥和初始化向量
key_hex = '31323334353637383930616263646566'
iv_hex = '1234567890abcdef1234567890abcdef'

# 16进制明文
data_hex = '31323334353637383930616263646566'

# 转换为bytes类型
key_b = bytes.fromhex(key_hex)
iv_b = bytes.fromhex(iv_hex)
data_b = bytes.fromhex(data_hex)

cipher = SM4(PKCS7)
cipher.key_generate(key_b)

ecb_result = cipher.encrypt_ecb(data_b)
print("ecb_result: ", ecb_result.hex())

cbc_result = cipher.encrypt_cbc(iv_b, data_b)
print("cbc_result: ", cbc_result.hex())
 评论