咱們首先把須要用到的算法呈現出來,最後咱們再考慮如何集合爲一個庫的方法,這一部分咱們就開始編寫一個新的算法:國家商用密碼標準SM3密碼算法。python
首先要明白SM3是一個什麼樣的東西:單向加密算法。也能夠稱之爲密碼哈希算法、雜湊算法、摘要算法,均可以指這類算法。顧名思義,這類算法只能加密不能解密,因此不是爲了直接保護數據的祕密性而存在的,不是讓使用者解密這串密文獲得原文而使用的。這類算法通常用於保護數據明文的完整性,抗篡改而產生。只要輸入的是一樣的明文,那麼輸出的密文(雜湊值/摘要值/哈希結果)就是同樣的,而找到一個字符串與預期字符串的輸出結果是同樣的,這目前在理論上不可實現,也就是說在目前的技術前提下,還不能作到篡改原文還能保持摘要值不受影響的事情。這類算法比較出名的好比MD5,SHA-1表明的系列算法,但其中部分算法如MD5已經被證明並不安全,因此在實際使用過程當中必定要使用當前覈準的優秀加密算法!算法
密碼雜湊算法在現代密碼學中起着重要的做用,它將任意長度的消息壓縮成固定長度的摘要。它是密碼學3大基礎算法之一(加密算法、數字簽名算法和雜湊算法),用於數據的完整性校驗、身份認證、數字簽名、密鑰推導、消息認證碼和隨機比特生成器等。安全
2012年,國家商用密碼管理辦公室發佈了SM3密碼雜湊算法爲密碼行業標準。2016年,國家標準化委員會公佈了SM3密碼雜湊算法爲國家標準。目前SM3已經提交ISO國際標準化組織,進入DIS階段。函數
如全部的密碼雜湊算法,SM3沒有密鑰這個概念,輸入數據長度爲任意,輸出長度爲32字節(256-bit)下面咱們按步驟一點點看:加密
廢話很少說,簡單的定義:設計
IV = "\x73\x80\x16\x6f\x49\x14\xb2\xb9\x17\x24\x42\xd7\xda\x8a\x06\x00\xa9\x6f\x30\xbc\x16\x31\x38\xaa\xe3\x8d\xee\x4d\xb0\xfb\x0e\x4e" T0_15 = "\x79\xcc\x45\x19" T16_63 = "\x7a\x87\x9d\x8a"
其中T0_15表示CF模塊中輪數0<=j<16時取用的值,T16_63表示輪數16<=j<64時取用的T值。IV爲初始狀態常量。code
對長度爲l(l<2^64)比特的消息m,首先將比特'1'填充至消息末尾,再添加k個'0',其中k知足l+k+1===448 mod512,取最小非負整數。再添加一個64比特字符串,該比特串是長度l的二進制表示。這樣,咱們就把明文填充至512比特的倍數長度。blog
看不太明白?代碼見!字符串
填充後的消息M就能夠按照512bit進行分組啦!依次分爲n個消息組,分別與初始常量IV按如下方式進行迭代:input
FOR i=0 TO (n-1): V[i+1] = CF( V[i] , B[i] ); ENDFOR
其中CF爲壓縮函數,V[0] = IV,B爲填充後的消息分組,迭代的輸出結果就是最後依次循環中被賦值的變量V[n]。
咱先不說那些複雜的公式,先看圖理解一下,這個部分的結構比較龐大:
右半部分與序列密碼中的部分非常類似,能夠將原來的數據進行充分的混合,隨着寄存器不停地進動,左端的信息會進入狀態更新區內,與數據V進行混合運算,再次充分混淆。這樣一來,每部分輸出的信息V都是本段數據和以前全部數據共同運算的結果,能夠表明這以前的數據是完整無修改的。公示詳述以下:
將最後一輪產生的V進行輸出,即獲得雜湊值運算結果!
這裏面顯然有幾個地方尚未定義,往下看:
在CF壓縮函數中涉及到的布爾函數和置換函數的詳細定義描述爲公式以下:
如此設計的布爾函數和置換函數讓每一個部分的數據都參與運算且運算效率極高,配合CF結構保證了運算的非線性特性明顯。
至此,整個算法的介紹到此爲止。
# 數字格式轉化 def int2str( num ) : out = "" out = out + chr( num//65536 ) + chr( (num%65536)//256 ) + chr( num%256 ) return out def str2hex( string ): out = "" for i in range ( 0 , len(string) ): out = out + " " + hex(ord( string[i] )) return out # 字符串異或運算,後面分別是字符串與、或、非運算 def strxor( message , key , len ): out = "" for i in range ( 0 , len ): ch = ord(message[i]) ^ ord(key[i]) out = out + chr(ch) return out def strand( message , key , len ): out = "" for i in range ( 0 , len ): ch = ord(message[i]) & ord(key[i]) out = out + chr(ch) return out def stror( message , key , len ): out = "" for i in range ( 0 , len ): ch = ord(message[i]) | ord(key[i]) out = out + chr(ch) return out def strnot( string , len ) : out = "" for i in range ( 0 , len ) : ch = ~ord(string[i]) out = out + chr(ch) return out # 字符串按比特向左移位 def strldc( string , bit ): byte = bit // 8 bit = bit % 8 out = "" if bit == 0 : out = string[byte:] + string[:byte] else : reg = string[byte:] + string[:byte+1] for i in range (0,len(reg)-1): out = out + chr(((ord(reg[i])*(2**bit))+(ord(reg[i+1])//(2**(8-bit))))%256) out = out[:len(string)] return out # 4字節模加運算 def stradd_4( str1 , str2 ) : out = "" num1 = ord(str1[0])*16777216+ord(str1[1])*65536+ord(str1[2])*256+ord(str1[3]) num2 = ord(str2[0])*16777216+ord(str2[1])*65536+ord(str2[2])*256+ord(str2[3]) add = (num1 + num2)%4294967296 out = out + chr(add//1677216) + chr((add%1677216)//65536) + chr((add%65536)//256) + chr(add%256) return out def functionFF( A , B , C , j ) : if j < 16 : FF = strxor( A , strxor( B , C , 4 ) , 4 ) if j > 15 : FF = stror( stror( strand(A,B,4) , strand(A,C,4) , 4 ) , strand(B,C,4) , 4) return FF def functionGG( A , B , C , j ) : if j<16 : GG = strxor( A , strxor( B , C , 4 ) , 4 ) if j > 15 : GG = stror( strand(A,B,4) , strand(strnot(A,4),C,4) , 4 ) return GG def functionP( string , mode ) : out = '' if mode == 0 : out = strxor( string , strxor( strldc(string,9) , strldc(string,17) , 4 ) , 4 ) if mode == 1 : out = strxor( string , strxor( strldc(string,15) , strldc(string,23) , 4 ) , 4 ) return out def functionCF( V , B ) : for i in range (0,68) : # 消息擴展過程 P = strxor( strxor( B[0:4] , B[28:32] , 4 ) , strldc( B[42:46] , 15 ) , 4) Badd = strxor( P , strxor( B[40:44] , strldc(B[12:16],7) , 4 ) , 4 ) out1 = strxor( B[0:4] , B[16:20] , 4 ) out = B[0:4] B = B[0:60] + Badd # 狀態更新過程 if i < 64 : if i < 16 : SS1 = strldc( stradd_4( strldc( V[0:4] , 12 ) , stradd_4( V[16:20] , strldc(T0_15,i%32) ) ) , 7 ) else : SS1 = strldc( stradd_4( strldc( V[0:4] , 12 ) , stradd_4( V[16:20] , strldc(T16_63,i%32) ) ) , 7 ) SS2 = strxor( SS1 , strldc( V[0:4] , 12 ) , 4 ) TT1 = stradd_4( stradd_4( functionFF( V[0:4] , V[4:8] , V[8:12] , i ) , V[12:16] ) , stradd_4( SS2 , out1 ) ) TT2 = stradd_4( stradd_4( functionGG( V[16:20] , V[20:24] , V[24:28] , i ) , V[28:32] ) , stradd_4( SS1 , out ) ) V = strxor( V , TT1 + V[0:4] + strldc(V[4:8],9) + V[8:12] + functionP(TT2,0) + V[16:20] + strldc(V[20:24],19) + V[24:28] , 32 ) return V IV = "\x73\x80\x16\x6f\x49\x14\xb2\xb9\x17\x24\x42\xd7\xda\x8a\x06\x00\xa9\x6f\x30\xbc\x16\x31\x38\xaa\xe3\x8d\xee\x4d\xb0\xfb\x0e\x4e" T0_15 = "\x79\xcc\x45\x19" T16_63 = "\x7a\x87\x9d\x8a" plain = input( "請輸入雜湊函數明文:" ) l = int2str( len(plain)*8 ) plain = plain + "\x80" k = 56 - (len(plain)%64) - 1 plain = plain + "\x00"*k plain = plain + l print( "plain :" , str2hex(plain) ) for i in range ( 0 , len(plain)//64-1 ) : IV = functionCF( IV , plain[64*l:64*l+64] ) hash_value = IV print( " hash :" , str2hex(hash_value) )
下面附上程序運行的效果圖:
因爲SM3的功能不具有多樣化特徵,且代碼長度短,這裏暫不呈現SM3類的封裝和使用過程,故代碼方面的展現到此爲止。
雜湊算法一直以來沒有嘗試去實現過,也就是說這是做者第一次嘗試去實現一個密碼雜湊算法,剛開始作的時候壓力仍是很大的,可是隨着算法步驟的一點點解讀,才發現其中的內部結構並不複雜,都是由計算機基本運算組成的,但讀懂文字解釋的步驟仍是至關有難度的。
論文依據:SM3密碼雜湊算法——王小云、於紅波。更多關於SM3密碼的分析與設計細節詳見論文。