SM3是中国自主研发的哈希算法,适合生成数据摘要,安全性较高。(单向加密,不可逆加密)

基础知识

SM3基础知识及加解密算法流程pdf下载

python实现

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
# -*- coding: utf-8 -*-
"""
Created on Sat Dec 14 21:40:07 2024

SM3 对输入的明文采用utf-8编码
对输出的密文采用十六进制编码

"""
#常量T
def T(j):
if j>=0 and j<=15:
return 0x79cc4519
elif j>=16 and j<=63:
return 0x7a879d8a
#布尔函数FF
def FF(X, Y, Z, j):
if j>=0 and j<=15:
result=X^Y^Z
return result # 转换为十六进制字符串,去掉前缀 '0x',并保持长度为 8 位(补零)
else:
result=(X & Y) | (X & Z) | (Y & Z)
return result # 转换为十六进制字符串,去掉前缀 '0x',并保持长度为 8 位(补零)
#布尔函数GG
def GG(X, Y, Z, j):
if j>=0 and j<=15:
result=X^Y^Z
return result # 转换为十六进制字符串,去掉前缀 '0x',并保持长度为 8 位(补零)
else:
result=(X & Y) | (~X & Z)
return result # 转换为十六进制字符串,去掉前缀 '0x',并保持长度为 8 位(补零)
#循环左移
def circular_left_shift(x_int, n): #将整数x循环左移n位
"""
对32位整数x进行n位的循环左移
:param x: 输入的整数 (8位16进制数)
:param n: 左移的位数
:return: 循环左移后的结果
"""
#因为n可能会大于32,此时相当于循环左移n%32位,所以提前对n进行处理
n=n%32
# x_int=int(x,16) #将x转化为整数 eg. x从'0a'变成了 10
result=((x_int << n) & 0xFFFFFFFF) | (x_int >> (32 - n)) #将x循环左移n位
# return f"{result:08x}" # 转换为十六进制字符串,去掉前缀 '0x',并保持长度为 8 位(补零)
return result
#置换函数P
def P0(X):
result=X^circular_left_shift(X,9)^circular_left_shift(X,17)
return result
def P1(X):
result=X^circular_left_shift(X,15)^circular_left_shift(X,23)
return result
#将明文字符串转换为二进制位串
def string_to_bitstring(s):
"""
将字符串转换为二进制位串
:param s: 输入字符串
:return: 由每个字符转换成的 8 位二进制数拼接成的字符串
"""
result=''.join(format(ord(char), '08b') for char in s)
return result
#添加填充位。使数据位长度为 448 bit , 剩余的 64 bit用于表示原始明文字符串长度 eg abc,占三字节 24 bit,故末尾的 64 bit表示 24
def add_padding_bits(bitstring):
bitstring_padding=bitstring+'1' #先在明文二进制位串后面补上1
if len(bitstring_padding)>448: #若数据长度大于等于448,继续填充
rule=(len(bitstring_padding)//512+1)*512+448 #填充后的长度
while len(bitstring_padding)!=rule: #若数据长度不足mod512后不为418,则补0,直至数据长度满足mod512后为448
bitstring_padding+='0'
else: #若数据长度小于448,则补0,使数据长度为448
while len(bitstring_padding)!=448:
bitstring_padding+='0'
bitstring_hex_list=[bitstring_padding[i:i+4] for i in range(0,len(bitstring_padding),4)] #截取每四位二进制数,存入列表,以便转为16进制
bitstring_hex=''.join(hex(int(x,2))[2:] for x in bitstring_hex_list) #将二进制位串的数据转为十六进制串,因为只能是一位,所以不用zfill
while len(bitstring_hex)%128!=0: #先将剩余的64bit全补为0,直至长度为512的整数倍
bitstring_hex+='0'
end_padding=hex(len(bitstring))[2:] #确定原始明文长度,十六进制,为了将原始明文长度添加到末尾,此处也不必zfill
bitstring_hex=bitstring_hex[:-len(end_padding)]+end_padding #将原始明文长度添加到末尾
return bitstring_hex
# 模 2**32 加法实现
def mod32_add(x, y, z, u):
# 转换为整数并模 2**32,只保留低 32 位
result = (x + y + z + u) & 0xFFFFFFFF
# 转换为十六进制字符串,去掉前缀 '0x',并保持长度为 8 位(补零)
# return f"{result:08x}"
return result
#SM3杂凑函数
def sm3_encode(bitstring_hex):
#将消息分组,每组128个十六进制,即512bit
bitstring_hex_group=[bitstring_hex[i:i+128] for i in range(0,len(bitstring_hex),128)] #将128位一组分割
#初始向量IV
IV = [0x7380166f, 0x4914b2b9, 0x172442d7, 0xda8a0600, 0xa96f30bc, 0x163138aa, 0xe38dee4d, 0xb0fb0e4e]
#生成字 W,对每个分组都进行
for bitstring_hex_i in bitstring_hex_group:
w=[bitstring_hex_i[i:i+8] for i in range(0,len(bitstring_hex_i),8)] #将每四个字节,构成一个字,存储到 w[]中,共 16个字
for j in range(16,68):
w_tem=P1(int(w[j-16],16)^int(w[j-9],16)^circular_left_shift(int(w[j-3],16),15))^circular_left_shift(int(w[j-13],16),7)^int(w[j-6],16)
w.append(format(w_tem, '08x')) #将w_tem转化为16进制字符串,并保持长度为 8 位(补零)
for j in range(0,64): #从w列表的第68位开始,存储w0',w1',...,w63'
w_tem=int(w[j],16)^int(w[j+4],16)
w.append(format(w_tem, '08x'))
#迭代压缩
A=[IV[len(IV)-8]]
B=[IV[len(IV)-7]]
C=[IV[len(IV)-6]]
D=[IV[len(IV)-5]]
E=[IV[len(IV)-4]]
F=[IV[len(IV)-3]]
G=[IV[len(IV)-2]]
H=[IV[len(IV)-1]]
for j in range(0,64):
if j!=0:
A=[int(i,16) for i in A]
B=[int(i,16) for i in B]
C=[int(i,16) for i in C]
D=[int(i,16) for i in D]
E=[int(i,16) for i in E]
F=[int(i,16) for i in F]
G=[int(i,16) for i in G]
H=[int(i,16) for i in H]
SS1=circular_left_shift(mod32_add(circular_left_shift(A[len(A)-1],12),E[len(E)-1],circular_left_shift(T(j),j),0),7)
SS2=SS1^circular_left_shift(A[len(A)-1],12)
TT1=mod32_add(FF(A[len(A)-1],B[len(B)-1],C[len(C)-1],j),D[len(D)-1],SS2,int(w[j+68],16))
TT2=mod32_add(GG(E[len(E)-1],F[len(F)-1],G[len(G)-1],j),H[len(H)-1],SS1,int(w[j],16))
D.append(C[len(C)-1])
C.append(circular_left_shift(B[len(B)-1],9))
B.append(A[len(A)-1])
A.append(TT1)
H.append(G[len(G)-1])
G.append(circular_left_shift(F[len(F)-1],19))
F.append(E[len(E)-1])
E.append(P0(TT2))
A=[format(i, '08x') for i in A]
B=[format(i, '08x') for i in B]
C=[format(i, '08x') for i in C]
D=[format(i, '08x') for i in D]
E=[format(i, '08x') for i in E]
F=[format(i, '08x') for i in F]
G=[format(i, '08x') for i in G]
H=[format(i, '08x') for i in H]

#更新IV
A=[int(i,16) for i in A]
B=[int(i,16) for i in B]
C=[int(i,16) for i in C]
D=[int(i,16) for i in D]
E=[int(i,16) for i in E]
F=[int(i,16) for i in F]
G=[int(i,16) for i in G]
H=[int(i,16) for i in H]
IV.append(A[len(A)-1]^IV[len(IV)-8])
IV.append(B[len(B)-1]^IV[len(IV)-8])
IV.append(C[len(C)-1]^IV[len(IV)-8])
IV.append(D[len(D)-1]^IV[len(IV)-8])
IV.append(E[len(E)-1]^IV[len(IV)-8])
IV.append(F[len(F)-1]^IV[len(IV)-8])
IV.append(G[len(G)-1]^IV[len(IV)-8])
IV.append(H[len(H)-1]^IV[len(IV)-8])
#将IV转化为16进制字符串,并保持长度为 8 位(补零)
IV=[format(i, '08x') for i in IV]
#将IV_hex拼接成16进制字符串
result=IV[len(IV)-8]+IV[len(IV)-7]+IV[len(IV)-6]+IV[len(IV)-5]+IV[len(IV)-4]+IV[len(IV)-3]+IV[len(IV)-2]+IV[len(IV)-1]
return result
#主程序
def main():
while True:
print("==================== SM3 加密======================")
print("[1] 加密")
print("[0] 退出")
print("====================================================")
try:
choice = int(input("请输入操作选项 (0 或 1 ): "))
if choice == 0:
print("\n\n程序已退出。感谢使用 SM3 加密工具!")
break
elif choice == 1:
# 加密流程
plaintext = input("请输入明文 (UTF-8 编码,任意长度): ")
#明文转化为二进制位字符串
bitstring = string_to_bitstring(plaintext)
#填充为512位二进制,并转化为16进制,共128个字符
bitstring_hex = add_padding_bits(bitstring)
if not plaintext:
raise ValueError("明文不能为空!")
#sha-1加密
ciphertext=sm3_encode(bitstring_hex)
print("\n加密结果 (十六进制):")
print(ciphertext)
else:
raise ValueError("无效选项")

except ValueError as e:
print(f"输入错误: {e}")
except Exception as e:
print(f"发生错误: {e}")
# =============================================================================
# # 示例 密码书 P318
# #输入明文
# plaintext = "abc"
# #明文转化为二进制位字符串
# bitstring = string_to_bitstring(plaintext)
# #填充为512位二进制,并转化为16进制,共128个字符
# bitstring_hex = add_padding_bits(bitstring)
# #SM3加密
# ciphertext=sm3_encode(bitstring_hex)
# print(f"输入字符串: {plaintext}")
# print(f"密文串: {ciphertext}")
# =============================================================================
if __name__=="__main__":
main()