SM3

一、概述

SM3密码杂凑算法是中国国家密码管理局颁布的一种密码Hash函数,它与SM4分组密码、SM2椭圆曲线公钥密钥一起都是中国商用密码的重要组成部分。

SM3是中华人民共和国政府采用的一种密码散列函数标准,由国家密码管理局于2010年12月17日发布。相关标准为“GM/T 0004-2012 《SM3密码杂凑算法》”。
在商用密码体系中,SM3主要用于数字签名及验证、消息认证码生成及验证、随机数生成等,其算法公开。据国家密码管理局表示,其安全性及效率与SHA-256相当。

对长度为 l (1 < l < image) 比特的消息m,SM3杂凑算法经过填充和迭代压缩,生成杂凑值,杂凑值长度为256比特。

注:字:长度为32的比特串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
IV = [0x7380166f, 0x4914b2b9, 0x172442d7, 0xda8a0600, 0xa96f30bc, 0x163138aa, 0xe38dee4d, 0xb0fb0e4e]

T = [0x79cc4519] * 16 + [0x7a879d8a] * 48

def rotate_left(x, n):
return ((x << n) | (x >> (32 - n))) & 0xFFFFFFFF

def FF(j, X, Y, Z):
if 0 <= j <= 15:
return X ^ Y ^ Z
else:
return (X & Y) | (X & Z) | (Y & Z)

def GG(j, X, Y, Z):
if 0 <= j <= 15:
return X ^ Y ^ Z
else:
return (X & Y) | (~X & Z)

def P0(X):
return X ^ rotate_left(X, 9) ^ rotate_left(X, 17)

def P1(X):
return X ^ rotate_left(X, 15) ^ rotate_left(X, 23)

二、填充

解释:

假设消息m 的长度为 l 比特。首先将比特“1”添加到消息的末尾,再添加k 个“0”,k是满足l + 1 + k ≡ 448 mod 512的最小的非负整数。然后再添加一个64位比特串,该比特串是长度 l 的二进制表示。填充后的消息m′ 的比特长度为512的倍数。

例如:对消息01100001 01100010 01100011,其长度 l =24,经填充得到比特串:

代码:
1
2
3
4
5
6
7
8
def sm3_padding(message):
message_length = len(message) * 8
message = bytearray(message)
message.append(0x80)
while (len(message) * 8) % 512 != 448:
message.append(0x00)
message.extend(message_length.to_bytes(8, byteorder='big'))
return message

三、迭代压缩

迭代过程

解释:

将填充后的消息m′按512比特进行分组:image

其中n=(l+k+65)/512

对m′按下列方式迭代:

image

消息扩散

解释:

将消息分组image按以下方法扩展生成132个字image, 用于压缩函数CF:

压缩函数

解释:

由SM3的压缩函数的算法可以看出,压缩函数进行了64次循环迭代。SM3的压缩函数CF把每一个512位的消息分组 image 压缩成256位。经过个数据分组之间的迭代处理后把 l 位的信息压缩成256位的hash值。其中布尔函数image(X,Y,Z)和image(X,Y,Z)是非线性函数,经过循环迭代后提供混淆作用。置换函数imageimage是线性函数,经过循环迭代后提供扩散作用,确保算法的安全性。

单块处理函数代码实现

  • 功能:对单个 512 位(64 字节)的数据块进行压缩处理。具体步骤如下:
    1. 消息扩展:将输入的 64 字节数据块转换为 68 个 32 位整数 W,并进一步生成 64 个 32 位整数 W1
    2. 初始化中间变量:将输入的中间哈希值 V 拆分为 8 个 32 位整数 A, B, C, D, E, F, G, H
    3. 64 轮迭代:根据常量 T[j]W[j]W1[j],以及 FFGGP0 等函数,更新中间变量的值。
    4. 输出结果:将迭代后的中间变量与输入的中间哈希值进行异或操作,得到新的中间哈希值。
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
def sm3_process_block(block, V):
W = []
for i in range(16):
W.append(int.from_bytes(block[i * 4:(i + 1) * 4], byteorder='big'))
for i in range(16, 68):
W.append(P1(W[i - 16] ^ W[i - 9] ^ rotate_left(W[i - 3], 15)) ^ rotate_left(W[i - 13], 7) ^ W[i - 6])
W1 = []
for i in range(64):
W1.append(W[i] ^ W[i + 4])
A, B, C, D, E, F, G, H = V
for j in range(64):
SS1 = rotate_left((rotate_left(A, 12) + E + rotate_left(T[j], j % 32)) & 0xFFFFFFFF, 7)
SS2 = SS1 ^ rotate_left(A, 12)
TT1 = (FF(j, A, B, C) + D + SS2 + W1[j]) & 0xFFFFFFFF
TT2 = (GG(j, E, F, G) + H + SS1 + W[j]) & 0xFFFFFFFF
D = C
C = rotate_left(B, 9)
B = A
A = TT1
H = G
G = rotate_left(F, 19)
F = E
E = P0(TT2)
return [(x ^ y) & 0xFFFFFFFF for x, y in zip([A, B, C, D, E, F, G, H], V)]

四、杂凑值

image

输出256比特的杂凑值y = ABCDEFGH。处理过程如下:

五、完整代码

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
IV = [0x7380166f, 0x4914b2b9, 0x172442d7, 0xda8a0600, 0xa96f30bc, 0x163138aa, 0xe38dee4d, 0xb0fb0e4e]
T = [0x79cc4519] * 16 + [0x7a879d8a] * 48

def rotate_left(x, n):
return ((x << n) | (x >> (32 - n))) & 0xFFFFFFFF

def FF(j, X, Y, Z):
if 0 <= j <= 15:
return X ^ Y ^ Z
else:
return (X & Y) | (X & Z) | (Y & Z)

def GG(j, X, Y, Z):
if 0 <= j <= 15:
return X ^ Y ^ Z
else:
return (X & Y) | (~X & Z)

#置换函数
def P0(X):
return X ^ rotate_left(X, 9) ^ rotate_left(X, 17)

def P1(X):
return X ^ rotate_left(X, 15) ^ rotate_left(X, 23)

#消息填充函数
def sm3_padding(message):
message_length = len(message) * 8
message = bytearray(message)
message.append(0x80)
while (len(message) * 8) % 512 != 448:
message.append(0x00)
message.extend(message_length.to_bytes(8, byteorder='big'))
return message

#单块处理函数
def sm3_process_block(block, V):
W = []
for i in range(16):
W.append(int.from_bytes(block[i * 4:(i + 1) * 4], byteorder='big'))
for i in range(16, 68):
W.append(P1(W[i - 16] ^ W[i - 9] ^ rotate_left(W[i - 3], 15)) ^ rotate_left(W[i - 13], 7) ^ W[i - 6])
W1 = []
for i in range(64):
W1.append(W[i] ^ W[i + 4])
A, B, C, D, E, F, G, H = V
for j in range(64):
SS1 = rotate_left((rotate_left(A, 12) + E + rotate_left(T[j], j % 32)) & 0xFFFFFFFF, 7)
SS2 = SS1 ^ rotate_left(A, 12)
TT1 = (FF(j, A, B, C) + D + SS2 + W1[j]) & 0xFFFFFFFF
TT2 = (GG(j, E, F, G) + H + SS1 + W[j]) & 0xFFFFFFFF
D = C
C = rotate_left(B, 9)
B = A
A = TT1
H = G
G = rotate_left(F, 19)
F = E
E = P0(TT2)
return [(x ^ y) & 0xFFFFFFFF for x, y in zip([A, B, C, D, E, F, G, H], V)]

#哈希计算函数
def sm3_hash(message):
message = sm3_padding(message)
V = IV
for i in range(0, len(message), 64):
block = message[i:i + 64]
V = sm3_process_block(block, V)
result = b''
for v in V:
result += v.to_bytes(4, byteorder='big')
return result.hex()


if __name__ == "__main__":
message = input("请输入要计算 SM3 哈希值的消息: ").encode()
hash_value = sm3_hash(message)
print("SM3 Hash:", hash_value)

六、示例

  1. 输入消息为“abc”

杂凑值:66c7f0f4 62eeedd9 d1f2d46b dc10e4e2 4167c487 5cf2f7a2 297da02b 8f4ba8e0

sm3.py

sm3_1.py

SM3在线加密工具