sm3密码杂凑算法是中国国家密码管理局颁布的一种密码Hash函数,它与sm4分组密码、sm2椭圆曲线公钥密钥一起都是中国商用密码的重要组成部分。
SM3密码Hash算法的输入数据长度为 l 比特,1≤ l ≤ 264-1,输出Hash值的长度为256比特。
SM3密码Hash函数使用以下常数与函数。
初始值IV=7380166f 4914b2b9 172442d7 da8a0600 a96f30bc 163138aa e38dee4d b0fb0e4e
#布尔函数
def FF_j(X,Y,Z,j):
if j<=15:
a = or_16(X,Y)
a = or_16(a,Z)
else:
a = and_Cal(X,Y)
b = and_Cal(X,Z)
c = and_Cal(Y,Z)
a = or_Cal(a,b)
a = or_Cal(a,c)
return a
#布尔函数
def GG_j(X, Y, Z, j):
if j <= 15:
a = or_16(X, Y)
a = or_16(a, Z)
else:
a = and_Cal(X,Y)
b = qufan(X)
b = and_Cal(b,Z)
a = or_Cal(a,b)
return a
置换函数: SM3密码Hash算法对数据进行填充和迭代压缩后生成Hash值。 对数据进行填充的目的是使填充后的数据长度为512的整数倍。后面进行的迭代压缩式对512位的数据块进行的,如果数据的长度不是512的整数倍,最后一块数据块将是短块,这将无法处理。 将填充后的信息m’ 按512比特进行分组:m’ = B(0) B(1) … B(n-1) ,其中 n = (l+k+65)/512 FOR i=0 TO n-1 其中:CF是压缩函数;V(0) 为256比特初始值IV;B(i) 为填充后的消息分组;迭代压缩的结果为V(n) 即为消息m的Hash值。 在对消息分组B(i) 进行迭代压缩之前,首先对其进行消息扩展。进行消息扩展有两个目的。目的之一是将16个字的消息分组B(i) 扩展成下式(1)的132个字,供压缩函数CF使用。目的之二是通过消息扩展把原消息位打乱,隐蔽了原信息之间的关联,增强了Hash函数的安全性。 令A,B,C,D,E,F,G,H为字寄存器,SS1,SS2,TT1,TT2为中间变量,压缩函数V(i+1) =CF(V(i) ,B(i) ),0 ≤ i ≤ n-1 计算过程描述如下, A B C D E F G H ← V ( n ) ABCDEFGH ←V^{(n)} ABCDEFGH←V(n) 注意:循环移位以字(32bit)处理,所以要模32后再移位,否则压缩函数在Tj<<
P0(X) = X ⨁ (X<<<9)⨁(X<<<17)
P1(X) = X ⨁ (X<<<15)⨁(X<<<23)
式中X为32位字,符号a<<#置换函数
def Replace_P1(X):
#X为32位字
X_15 = Cyc_shift(X,15) #循环移位
X_23 = Cyc_shift(X,23)
a = or_16(X,X_15)
a = or_16(a,X_23)
return a
#置换函数
def Replace_P0(X):
#X为32位字
X_9 = Cyc_shift(X,9)
X_17 = Cyc_shift(X,17)
a = or_16(X,X_9)
a = or_16(a,X_17)
return a
2.算法描述
(1)填充
假设消息m的长度为l 比特。首先将比特“1”添加到消息m的末尾,再添加k个“0”。其中,k 是满足 l+1+k=448 mod 512 的最小非负整数。然后再添加一个64位比特串,该比特串是长度l 的二进制表示。填充后的消息m的比特串长度一定是512的倍数。
例如,对消息 01100001 01100010 01100011,其长度为l=24,经填充后得到比特串:
#填充函数
def filling(m):
#消息m是一个16进制字符串
#直接加16进制比较好
#61626364616263646162636461626364616263646162636461626364616263646162636461626364616263646162636461626364616263646162636461626364
#a = int(m,16)
#b = bin(a)[2:]#消息转换为二进制
length_b = len(m)*4#记录消息的长度
#s1='{:04b}'.format(s1)
b = m
b = b + '8'#补 1
c = len(b)%128
c = 112 - c#补 0 的个数
d = '0'*c
b = b + d#补 0
length_m = '{:016x}'.format(length_b)#也是16进制
b = b + length_m#填充完毕
#b = int(b)
#b = hex(b)[2:]
return b
(2)迭代压缩
对m’ 按下列方式迭代压缩:
V(i+1) = CF(V(i),B(i) )
ENDFOR(3)消息扩展
W 0 , W 1 , . . . W 67 , W 0 ′ , W 1 ′ , . . . W 63 ′ − − − − − − ( 1 ) W_0,W_1,... W_{67},W'_0,W'_1,... W'_{63} ------(1) W0,W1,...W67,W0′,W1′,...W63′−−−−−−(1)
消息扩展的步骤如下:
消息分组B(i) 经消息扩展后就可以进行迭代压缩了。#扩展函数
def expand(m,n):#n代表是第几组消息,消息之间没有关系,不用迭代
B = fenzu(m)#列表
W = ['0' for i in range(68)]
W_0 = ['0' for i in range(64)]
for i in range(int(len(B[n])/8)):#128/8=16个字
w = B[n][i*8:(i+1)*8]
W[i] = w
for j in range(16,68):
a = or_16(W[j-16],W[j-9])
W_j_3 = Cyc_shift(W[j-3],15)
#print(W_j_3)
a = or_16(a,W_j_3)
a = Replace_P1(a)
#print(a)
W_j_13=Cyc_shift(W[j-13],7)
a = or_16(a,W_j_13)
a = or_16(a,W[j-6])
W[j]=a
#return W
for j in range(64):
W_0[j]=or_16(W[j],W[j+4])
return W,W_0
(4)压缩函数
其中压缩函数中的+运算为mod 232 算术加运算,字的存储为大端格式。
由SM3的压缩函数的算法可以看出,压缩函数进行了64次循环迭代。SM3的压缩函数CF把每一个512位的消息分组 B(i) 压缩成256位。经过个数据分组之间的迭代处理后把 l 位的信息压缩成256位的hash值。其中布尔函数FFj(X,Y,Z)和GGj(X,Y,Z)是非线性函数,经过循环迭代后提供混淆作用。置换函数P0(X)和P1(X)是线性函数,经过循环迭代后提供扩散作用,确保算法的安全性。#压缩函数
def CF(m,n,k):
w = expand(m, n)
W = w[0]
W_0 = w[1]
A=V[k][0:8]
B=V[k][8:16]
C=V[k][16:24]
D=V[k][24:32]
E=V[k][32:40]
F=V[k][40:48]
G=V[k][48:56]
H=V[k][56:64]
#print(W_0)
all=''
for j in range(64):
#print(E)
b= a = Cyc_shift(A,12)
#t = b
T = T_j(j)
#
T = Cyc_shift(T,j)#忘记移位了,移位问题
a = add(a,E)
a = add(a,T)
SS1 = Cyc_shift(a,7)
SS2 = or_16(SS1,b)
b = FF_j(A,B,C,j)
b = add(b,D)
b = add(b,SS2)
TT1 = add(b,W_0[j]) #
b = GG_j(E,F,G,j)
b = add(b, H)
b = add(b, SS1)
TT2 = add(b, W[j]) #
D = C
C = Cyc_shift(B,9)
B = A
A = TT1#
H = G
G = Cyc_shift(F,19)
F = E
E = Replace_P0(TT2) #
all = A+B+C+D+E+F+G+H
#print(all)
#V[k+1]=or_16(all,V[k])
#return V[k+1]
#print(t)
#return all
V[k+1]=or_16(V[k],all)
(5)杂凑值
输出256比特的杂凑值 y = ABCDEFGH。处理过程如下:
实现示例:
512比特消息:
61626364 61626364 61626364 61626364 61626364 61626364 61626364 61626364
61626364 61626364 61626364 61626364 61626364 61626364 61626364 61626364
填充后消息:
61626364 61626364 61626364 61626364 61626364 61626364 61626364 61626364
61626364 61626364 61626364 61626364 61626364 61626364 61626364 61626364
80000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
80000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
杂凑值:
debe9ff9 2275b8a1 38604889 c18e5a4d 6fdb70e5 387e5765 293dcba3 9c0c5732
全部代码:sm3.py
m='61626364616263646162636461626364616263646162636461626364616263646162636461626364616263646162636461626364616263646162636461626364'
IV='7380166f4914b2b9172442d7da8a0600a96f30bc163138aae38dee4db0fb0e4e'
#填充函数
def filling(m):
#消息m是一个16进制字符串
#直接加16进制比较好
#61626364616263646162636461626364616263646162636461626364616263646162636461626364616263646162636461626364616263646162636461626364
#a = int(m,16)
#b = bin(a)[2:]#消息转换为二进制
length_b = len(m)*4#记录消息的长度
#s1='{:04b}'.format(s1)
b = m
b = b + '8'#补 1
c = len(b)%128
c = 112 - c#补 0 的个数
d = '0'*c
b = b + d#补 0
length_m = '{:016x}'.format(length_b)#也是16进制
b = b + length_m#填充完毕
#b = int(b)
#b = hex(b)[2:]
return b
#分组函数
def fenzu(m):
m = filling(m)
len_m = len(m)/128
m_list = []
for i in range(int(len_m)):
a = m[0+128*i:+128*(i+1)]
m_list.append(a)
return m_list
#扩展函数
def expand(m,n):#n代表是第几组消息,消息之间没有关系,不用迭代
B = fenzu(m)#列表
W = ['0' for i in range(68)]
W_0 = ['0' for i in range(64)]
for i in range(int(len(B[n])/8)):#128/8=16个字
w = B[n][i*8:(i+1)*8]
W[i] = w
for j in range(16,68):
a = or_16(W[j-16],W[j-9])
W_j_3 = Cyc_shift(W[j-3],15)
#print(W_j_3)
a = or_16(a,W_j_3)
a = Replace_P1(a)
#print(a)
W_j_13=Cyc_shift(W[j-13],7)
a = or_16(a,W_j_13)
a = or_16(a,W[j-6])
W[j]=a
#return W
for j in range(64):
W_0[j]=or_16(W[j],W[j+4])
return W,W_0
#置换函数
def Replace_P1(X):
#X为32位字
X_15 = Cyc_shift(X,15) #循环移位
X_23 = Cyc_shift(X,23)
a = or_16(X,X_15)
a = or_16(a,X_23)
return a
#置换函数
def Replace_P0(X):
#X为32位字
X_9 = Cyc_shift(X,9)
X_17 = Cyc_shift(X,17)
a = or_16(X,X_9)
a = or_16(a,X_17)
return a
#异或函数
def or_16(A,B):
A = int(A,16)
B = int(B,16)
C = A ^ B
C = '{:08x}'.format(C)
return C
#循环移位函数
def Cyc_shift(W,n):
a = int(W,16)
a = '{:032b}'.format(a)
while n>=32:
n=n-32
a = a[n:] + a[:n]
a = int(a,2)
a = '{:08x}'.format(a)
return a
#常量Tj
def T_j(j):
if j<=15:
T_j='79cc4519'
else:
T_j='7a879d8a'
return T_j
#mod 2^32 算术加运算
def add(x,y):
x = int(x,16)
x = '{:032b}'.format(x)
x = list(x)
y = int(y, 16)
y = '{:032b}'.format(y)
y = list(y)
#print(x)
#print(y)
a = [0 for _ in range(32)]
carry = 0
for i in range(32):
m = int(x[31-i])+int(y[31-i])+carry
if m>=2:
d=m-2
a[31-i]=str(d)
carry=1
else:
carry=0
d=m
a[31 - i] = str(d)
#print(a)
b=''.join(a)
b=int(b,2)
b='{:08x}'.format(b)
return b
#布尔函数
def FF_j(X,Y,Z,j):
if j<=15:
a = or_16(X,Y)
a = or_16(a,Z)
else:
a = and_Cal(X,Y)
b = and_Cal(X,Z)
c = and_Cal(Y,Z)
a = or_Cal(a,b)
a = or_Cal(a,c)
return a
#布尔函数
def GG_j(X, Y, Z, j):
if j <= 15:
a = or_16(X, Y)
a = or_16(a, Z)
else:
a = and_Cal(X,Y)
b = qufan(X)
b = and_Cal(b,Z)
a = or_Cal(a,b)
return a
#与运算函数
def and_Cal(a,b):
a = int(a,16)
b = int(b,16)
a_b = a & b
a_b = '{:08x}'.format(a_b)
return a_b
#或运算函数
def or_Cal(a,b):
a = int(a, 16)
b = int(b, 16)
a_b = a | b
a_b = '{:08x}'.format(a_b)
return a_b
#按位取反函数
def qufan(A):
A = int(A,16)
A = '{:032b}'.format(A)
A = list(A)
for i in range(32):
if A[i]=='0':
A[i]='1'
else:
A[i]='0'
A = ''.join(A)
A = int(A,2)
A = '{:08x}'.format(A)
return A
#压缩函数
m_list = fenzu(m)
m_len = len(m_list)
V = ['0' for i in range(m_len+1)]
V[0]=IV
#压缩函数
def CF(m,n,k):
w = expand(m, n)
W = w[0]
W_0 = w[1]
A=V[k][0:8]
B=V[k][8:16]
C=V[k][16:24]
D=V[k][24:32]
E=V[k][32:40]
F=V[k][40:48]
G=V[k][48:56]
H=V[k][56:64]
#print(W_0)
all=''
for j in range(64):
#print(E)
b= a = Cyc_shift(A,12)
#t = b
T = T_j(j)
#
T = Cyc_shift(T,j)#忘记移位了,移位问题
a = add(a,E)
a = add(a,T)
SS1 = Cyc_shift(a,7)
SS2 = or_16(SS1,b)
b = FF_j(A,B,C,j)
b = add(b,D)
b = add(b,SS2)
TT1 = add(b,W_0[j]) #
b = GG_j(E,F,G,j)
b = add(b, H)
b = add(b, SS1)
TT2 = add(b, W[j]) #
D = C
C = Cyc_shift(B,9)
B = A
A = TT1#
H = G
G = Cyc_shift(F,19)
F = E
E = Replace_P0(TT2) #
all = A+B+C+D+E+F+G+H
#print(all)
#V[k+1]=or_16(all,V[k])
#return V[k+1]
#print(t)
#return all
V[k+1]=or_16(V[k],all)
#print(CF(m,0,0))
#print(V)
def hash(m=m):
for i in range(m_len):
v_n=CF(m,i,i)
print(V[-1])
return V[-1]
#b=''
#b=a[0:8]+'\0'+a[8:16]+'\0'+a[16:24]+'\0'+a[24:32]+'\0'+a[32:40]+'\0'+a[40:48]+'\0'+a[48:56]+'\0'+a[56:64]
#print(b)
hash()
#print('输入的消息m是:\n',m)
#print('消息m的hash值为:\n',b)