基于Python的SM3 Hash及Hmac

Hash及Hmac

Hash算法,主要用于获取摘要值,由于其不可逆向,从而保证明文的完整性。
Hmac在Hash的基础上引入了密钥,在Hmac的计算过程中通过两次异或,两次Hash得出消息认证码。目前SSL协议、IPsec协议、SSH协议等在通信过程中都使用了Hmac算法保护数据的完整性。
Hmac比Hash多了一层安全性,使用Hash算法时如明文与摘要同时被篡改,则无法确保其完整性。使用Hmac算法时,在无法获取密钥的情况下即使篡改了明文与摘要,接收方也能通过Hmac算法判断其完整性遭到了破坏。

Hash实现

1.填充

Hash算法对数据输入长度无要求,因为在接受到原始输入后,必须对其进行填充使其长度为L的倍数,在SM3算法中,L为64(Byte)。
SM3算法的填充方式为:
首先将明文按照512bit分组,下文中提到的64bit的L为分组前整体数据的长度。
长度小于448bit时。
在明文后填充一个“1”,在尾部填充明文的长度L(L填充在最尾部,如L小于64bit,在高位填充0),然后在填充的“1”和尾部64bit的L之间填充0,使数据整体长度达到512bit。

输入数据(16进制)“616263>>>"61626380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018"

长度大于或等于448bit,但小于512bit时
在明文后填充一个“1”,然后在尾部填充0,使长度达到512bit。然后填充一组512bit数据(448个0+64bit的L)

输入数据(16进制)“616263800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000>>>6162638000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200

长度大于512bit时,判断最后一组数据的长度,然后按照上面的方式进行填充

输入数据(16进制)“616263800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000”
分组后的数据:
>>>['61626380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018', '61']
填充后的数据:
>>>['61626380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018', '61800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000208']

长度等于512bit或为512bit的倍数时
在尾部填充一个“1”,447个“0”,以及64bit的L。

输入数据(16进制)“6162638000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001861626380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018”
分组后的数据:
>>>['61626380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018', '61626380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018']
填充后的数据:
>>>['61626380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018', '61626380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018', '80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400']

代码实现

def group(list, n):##分组
    for i in range(0, len(list), n):
        yield list[i:i + n]
def sm3(data):
    l = len(data)//2
    byte = '{0:x}'.format(int(l*8))
    data_list = []
    [data_list.append(i) for i in group(data, 128)]
    m = l%64
    if m < 56 and m != 0:
        data_list[-1] = (data_list[-1]+'80').ljust(112,'0')+str(byte).rjust(16,'0')
    elif m >= 56:
        data_list[-1] = (data_list[-1]+'80').ljust(128,'0') 
        data_list.append(112*'0'+str(byte).rjust(16,'0'))
    elif m == 0:
        data_list.append('80'+110*'0'+str(byte).rjust(16,'0'))
    return data_list

2.迭代压缩

迭代压缩过程中主要使用异或、或、与、非运算以及32位循环左移,这里不再描述具体运算规则。
在迭代压缩过程中,为了方便,所有值都改为了10进制。
IV和TJ为GB-T32905给出的初始值与常量。

def xor(a,b):
    a1 = int(a,16)
    b1 = int(b,16)
    A = '{:08x}'.format(int(a1^b1))
    return A
def left_hex(list,n):
    out1 = '{:032b}'.format(int(list,16))
    out2 = out1[n:] + out1[:n]
    out_list = '{:08x}'.format(int(out2,2))
    return out_list
def left_int(list,n):
    out1 = '{:032b}'.format(list)
    out2 = out1[n:] + out1[:n]
    out_list = int(out2,2)
    return out_list 
IV = [1937774191, 1226093241, 388252375, 3666478592,2842636476, 372324522, 3817729613, 2969243214,]
Tj =[
    2043430169, 2043430169, 2043430169, 2043430169, 2043430169, 2043430169,
    2043430169, 2043430169, 2043430169, 2043430169, 2043430169, 2043430169,
    2043430169, 2043430169, 2043430169, 2043430169, 2055708042, 2055708042,
    2055708042, 2055708042, 2055708042, 2055708042, 2055708042, 2055708042,
    2055708042, 2055708042, 2055708042, 2055708042, 2055708042, 2055708042,
    2055708042, 2055708042, 2055708042, 2055708042, 2055708042, 2055708042,
    2055708042, 2055708042, 2055708042, 2055708042, 2055708042, 2055708042,
    2055708042, 2055708042, 2055708042, 2055708042, 2055708042, 2055708042,
    2055708042, 2055708042, 2055708042, 2055708042, 2055708042, 2055708042,
    2055708042, 2055708042, 2055708042, 2055708042, 2055708042, 2055708042,
    2055708042, 2055708042, 2055708042, 2055708042
]
def FFj(X, Y, Z, j):
    if 0 <= j and j < 16:
        return X ^ Y ^ Z 
    elif 16 <= j and j < 64:
        return (X & Y) | (X & Z) | (Y & Z)  

def GGj(X, Y, Z, j):
    if 0 <= j and j < 16:
        return X ^ Y ^ Z
    elif 16 <= j and j < 64:
        return (X & Y) | ((~ X) & Z)  

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

def P1(X):
    return xor(xor(X ,left_hex(X,15)),left_hex(X,23))

def CF(V,data):
    W = []
    W1 = []
    [W.append(data[i*8:(i+1)*8]) for i in range(16)]
    for i in range(16,68):
        w_in = xor(xor(P1(xor(xor(W[i-16],W[i-9]),left_hex(W[i-3],15))),left_hex(W[i-13],7)),W[i-6])
        w_out = '{:08x}'.format(int(w_in,16))
        W.append(w_out)
    [W1.append(xor(W[i],W[i+4])) for i in range(64)]
    A,B,C,D,E,F,G,H = V
    for i in range(0,64):
        ss1 = left_int((left_int(A,12)+E+left_int(Tj[i],i%32) & 0xffffffff),7)
        ss2 = ss1^(left_int(A,12))
        tt1 = (FFj(A,B,C,i) + D + ss2 + int(W1[i],16)) & 0xffffffff
        tt2 = (GGj(E,F,G,i) + H + ss1 + int(W[i],16)) & 0xffffffff
        D = C
        C = left_int(B, 9)
        B = A
        A = tt1
        H = G
        G = left_int(F, 19)
        F = E
        E = P0(tt2)
    outV = [A ^ V[0], B ^ V[1], C ^ V[2], D ^ V[3],E ^ V[4], F ^ V[5], G ^ V[6], H ^ V[7]]
    return outV

Hmac实现

def sm3_hmac(data,key):
    l = len(key)//2
    if l >64:
        key = sm3(key)
    else:pass
    key = key.ljust(128,'0')
    opad = '5c'*64
    ipad = '36'*64
    ipadkey = '%x'%(int(key,16)^int(ipad,16))
    M = sm3(ipadkey+data)
    opadkey = '%x'%(int(key,16)^int(opad,16))
    out_data = sm3(opadkey+M)
    return out_data

完整代码

IV = [1937774191, 1226093241, 388252375, 3666478592,2842636476, 372324522, 3817729613, 2969243214,]
Tj =[
    2043430169, 2043430169, 2043430169, 2043430169, 2043430169, 2043430169,
    2043430169, 2043430169, 2043430169, 2043430169, 2043430169, 2043430169,
    2043430169, 2043430169, 2043430169, 2043430169, 2055708042, 2055708042,
    2055708042, 2055708042, 2055708042, 2055708042, 2055708042, 2055708042,
    2055708042, 2055708042, 2055708042, 2055708042, 2055708042, 2055708042,
    2055708042, 2055708042, 2055708042, 2055708042, 2055708042, 2055708042,
    2055708042, 2055708042, 2055708042, 2055708042, 2055708042, 2055708042,
    2055708042, 2055708042, 2055708042, 2055708042, 2055708042, 2055708042,
    2055708042, 2055708042, 2055708042, 2055708042, 2055708042, 2055708042,
    2055708042, 2055708042, 2055708042, 2055708042, 2055708042, 2055708042,
    2055708042, 2055708042, 2055708042, 2055708042
]
def group(list, n):##分组
    for i in range(0, len(list), n):
        yield list[i:i + n]
def xor(a,b):
    a1 = int(a,16)
    b1 = int(b,16)
    A = '{:08x}'.format(int(a1^b1))
    return A
def left_hex(list,n):
    out1 = '{:032b}'.format(int(list,16))
    out2 = out1[n:] + out1[:n]
    out_list = '{:08x}'.format(int(out2,2))
    return out_list
def left_int(list,n):
    out1 = '{:032b}'.format(list)
    out2 = out1[n:] + out1[:n]
    out_list = int(out2,2)
    return out_list 
def FFj(X, Y, Z, j):
    if 0 <= j and j < 16:
        return X ^ Y ^ Z 
    elif 16 <= j and j < 64:
        return (X & Y) | (X & Z) | (Y & Z)  

def GGj(X, Y, Z, j):
    if 0 <= j and j < 16:
        return X ^ Y ^ Z
    elif 16 <= j and j < 64:
        return (X & Y) | ((~ X) & Z)  

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

def P1(X):
    return xor(xor(X ,left_hex(X,15)),left_hex(X,23))

def CF(V,data):
    W = []
    W1 = []
    [W.append(data[i*8:(i+1)*8]) for i in range(16)]
    for i in range(16,68):
        w_in = xor(xor(P1(xor(xor(W[i-16],W[i-9]),left_hex(W[i-3],15))),left_hex(W[i-13],7)),W[i-6])
        w_out = '{:08x}'.format(int(w_in,16))
        W.append(w_out)
    [W1.append(xor(W[i],W[i+4])) for i in range(64)]
    A,B,C,D,E,F,G,H = V
    for i in range(0,64):
        ss1 = left_int((left_int(A,12)+E+left_int(Tj[i],i%32) & 0xffffffff),7)
        ss2 = ss1^(left_int(A,12))
        tt1 = (FFj(A,B,C,i) + D + ss2 + int(W1[i],16)) & 0xffffffff
        tt2 = (GGj(E,F,G,i) + H + ss1 + int(W[i],16)) & 0xffffffff
        D = C
        C = left_int(B, 9)
        B = A
        A = tt1
        H = G
        G = left_int(F, 19)
        F = E
        E = P0(tt2)
    outV = [A ^ V[0], B ^ V[1], C ^ V[2], D ^ V[3],E ^ V[4], F ^ V[5], G ^ V[6], H ^ V[7]]
    return outV        
def sm3(data):
    l = len(data)//2
    byte = '{0:x}'.format(int(l*8))
    data_list = []
    [data_list.append(i) for i in group(data, 128)]
    m = l%64
    if m < 56 and m != 0:
        data_list[-1] = (data_list[-1]+'80').ljust(112,'0')+str(byte).rjust(16,'0')
    elif m >= 56:
        data_list[-1] = (data_list[-1]+'80').ljust(128,'0') 
        data_list.append(112*'0'+str(byte).rjust(16,'0'))
    elif m == 0:
        data_list.append('80'+110*'0'+str(byte).rjust(16,'0'))
	V = IV
    for i in range(0,len(data_list)):
        V = CF(V,data_list[i])
    for i in  range(len(V)):
        V[i] = '{:08x}'.format(V[i])
    return ''.join(V)
def sm3_hmac(data,key):
    l = len(key)//2
    if l >64:
        key = sm3(key)
    else:pass
    key = key.ljust(128,'0')
    opad = '5c'*64
    ipad = '36'*64
    ipadkey = '%x'%(int(key,16)^int(ipad,16))
    M = sm3(ipadkey+data)
    opadkey = '%x'%(int(key,16)^int(opad,16))
    out_data = sm3(opadkey+M)
    return out_data

版权声明:本文为weixin_46491183原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。