前面一篇中咱們分析了SHA的原理,而且以SHA1爲例實現了相關的算法,在這一片中咱們將進一步分析SHA2並實現之。算法
1、SHA簡述數組
前面的篇章中咱們已經說明過,SHA實際包括有一系列算法,分別是SHA-一、SHA-22四、SHA-25六、SHA-384以及SHA-512。而咱們所說的SHA2實際是對後面4中的統稱。各類SHA算法的數據比較以下表,其中的長度單位均爲位:安全
從上表中咱們不難發現,SHA-224和SHA-25六、SHA-384和SHA-512在消息長度、分組長度、計算字長以及計算步驟各方面分別都是一致的。事實上一般認爲SHA-224是SHA-256的縮減版,而SHA-384是SHA-512的縮減版。因此在接下來的討論中,咱們把SHA-224和SHA-256做爲一組,而把SHA-384和SHA-512做爲另外一組來討論。在這一篇咱們先來分析和實現SHA-224和SHA-256算法。函數
2、消息的填充與解析工具
在這裏咱們討論的散列函數用於在計算機中將根據做爲輸入消息或者數據文件生成其對應的信息摘要。消息或數據文件一般被做爲是位字符串。消息的長度是消息中的比特數(空消息的長度爲0)。若是消息中的比特數是8的倍數,那麼咱們就能夠用十六進制來表示消息的緊湊性。消息填充的目的是爲了在消息填充後,在SHA-224和SHA-256中消息的長度正好是512位的整數倍。ui
接下來咱們說明消息或者數據文件將如何實現填充。總的來講就是,先添加一個「1」,再後跟多個「0」,而後再追加一個64位的消息長度信息,使得填充完成後的消息長度正好是512位的整數倍。追加的64位的消息長度信息是原始消息的位長,填充完成的消息會被分紅512位的消息分組。編碼
對於SHA-224和SHA-256來講消息的最大長度L<264,在對消息進行散列運算以前須要對消息作相應的填充處理。spa
首先,在原始信息以後填充一個「1」,例如:若是原始信息是"01010000",完成這一填充以後就是 "010100001"。3d
接下來,在完成上一步填充後,在其後面需天充必定數量的「0」,數量記爲K,則K的取值必須是知足下述表達式的最小非負整數值。code
( L + 1 + K ) mod 512 = 448
最後,在填充完必的消息後,追加64位的原始消息長度,由於消息的長度不會超過264位,因此其長度數據的值不會超過64位。填充完畢後,全部的消息分組都將是一個512位。
3、迭代函數與常數
SHA算法這類散列算法的計算過程都須要用到邏輯函數和計算常量。但因爲具體算法的不一樣所使用的具體的函數和常數略有差異。咱們在前面的篇章中說過MD5和SHA1,它們都有4個邏輯函數,而在SHA2的一系列算法中都採用了6個邏輯函數。接下來將說明SHA-224和SHA-256的邏輯函數和常量。
SHA-224和SHA-256採用6個邏輯函數,每一個函數均基於32位字運算,這些輸入的32位字咱們記爲x、y、z,一樣的這些函數的計算結果也是一個32位字。這些邏輯函數表示以下:
CH( x, y, z) = (x AND y) XOR ( (NOT x) AND z)
MAJ( x, y, z) = (x AND y) XOR (x AND z) XOR (y AND z)
BSIG0(x) = ROTR^2(x) XOR ROTR^13(x) XOR ROTR^22(x)
BSIG1(x) = ROTR^6(x) XOR ROTR^11(x) XOR ROTR^25(x)
SSIG0(x) = ROTR^7(x) XOR ROTR^18(x) XOR SHR^3(x)
SSIG1(x) = ROTR^17(x) XOR ROTR^19(x) XOR SHR^10(x)
SHA-224和SHA-256採用相同的,64個32位的常數序列。一般記爲:K0、K一、……、K63,這些常數的取值是前64個質數的立方根的小數部分的前32位。這些數以16進製表示以下:
428a2f98 71374491 b5c0fbcf e9b5dba5 3956c25b 59f111f1 923f82a4 ab1c5ed5
d807aa98 12835b01 243185be 550c7dc3 72be5d74 80deb1fe 9bdc06a7 c19bf174
e49b69c1 efbe4786 0fc19dc6 240ca1cc 2de92c6f 4a7484aa 5cb0a9dc 76f988da
983e5152 a831c66d b00327c8 bf597fc7 c6e00bf3 d5a79147 06ca6351 14292967
27b70a85 2e1b2138 4d2c6dfc 53380d13 650a7354 766a0abb 81c2c92e 92722c85
a2bfe8a1 a81a664b c24b8b70 c76c51a3 d192e819 d6990624 f40e3585 106aa070
19a4c116 1e376c08 2748774c 34b0bcb5 391c0cb3 4ed8aa4a 5b9cca4f 682e6ff3
748f82ee 78a5636f 84c87814 8cc70208 90befffa a4506ceb bef9a3f7 c67178f2
4、計算過程
前面,咱們已經介紹了消息的預處理及散列邏輯函數,接下來咱們將說明摘要的計算過程。
每一個安全散列函數的輸出,在應用到一個分爲N個分組的消息後,結果記爲散列量H(N)。對於SHA-224和SHA-256,H(i)能夠被認爲是8個32位的字,記爲:H(i)0、H(i)一、…、H(i)7。
散列字被初始化爲一個特定的值,並在處理完每個消息分組後對它進行更新,並在處理最後一個塊後將其鏈接起來以產生輸出。對於SHA-256,全部的H(N)變量都是串聯的,而SHA-224散列值是經過最後鏈接時,省略一些而產生的。
接下來咱們說明一下SHA-224和SHA-256的計算過程。首先初始化連接變量。對於SHA-224來講,初始散列值H(0)由如下8個32位的十六進制數組成:
H(0)0 = c1059ed8
H(0)1 = 367cd507
H(0)2 = 3070dd17
H(0)3 = f70e5939
H(0)4 = ffc00b31
H(0)5 = 68581511
H(0)6 = 64f98fa7
H(0)7 = befa4fa4
而對於SHA-256來講,初始散列值H(0)由如下8個32位的十六進制數組成。這些字由前8個質數的平方根的小數部分的錢32位組成。
H(0)0 = 6a09e667
H(0)1 = bb67ae85
H(0)2 = 3c6ef372
H(0)3 = a54ff53a
H(0)4 = 510e527f
H(0)5 = 9b05688c
H(0)6 = 1f83d9ab
H(0)7 = 5be0cd19
接下來咱們描述一下摘要計算,SHA-224和SHA-256在消息分組執行相同的處理,只在初始化H(0)和如何生成最終輸出的過程當中有所不一樣。SHA-224和SHA-256能夠用來散列處理長度爲L位的消息,其中0 < L< = 264。算法使用一個64個32位字的消息列表, 8個工做變量32位以及8個32位字的散列值。
消息列表每32位分爲一個子分組,被標記爲W0、W一、…、W63。8個工做變量分別爲a、b、c、d、e、f、g和h,8個散列值被標記爲h(i)0、h(i)一、…、H(i)7,並保留初始散列值H(0),替換爲每個連續的中間散列值(在處理完每一個消息分組後) H(i),並以最終的散列值H(N)結束,在處理完全部N塊後。它們還使用兩個臨時變量T1和T2。
從前面咱們知道,填充完了以後消息被分爲了512位的消息分組。每一個分組被分爲16個32位的子分組,記爲:M(i)0、M(i)一、...、M(i)15。將對N個消息分組進行以下操做。
a、64個消息列表的生成
For t = 0 to 15
Wt = M(i)t
For t = 16 to 63
Wt = SSIG1(W(t-2)) + W(t-7) + SSIG0(w(t-15)) + W(t-16)
b、初始化工做變量
a = H(i-1)0
b = H(i-1)1
c = H(i-1)2
d = H(i-1)3
e = H(i-1)4
f = H(i-1)5
g = H(i-1)6
h = H(i-1)7
c、執行散列計算
For t = 0 to 63
T1 = h + BSIG1(e) + CH(e,f,g) + Kt + Wt
T2 = BSIG0(a) + MAJ(a,b,c)
h = g
g = f
f = e
e = d + T1
d = c
c = b
b = a
a = T1 + T2
d、計算中間散列值
H(i)0 = a + H(i-1)0
H(i)1 = b + H(i-1)1
H(i)2 = c + H(i-1)2
H(i)3 = d + H(i-1)3
H(i)4 = e + H(i-1)4
H(i)5 = f + H(i-1)5
H(i)6 = g + H(i-1)6
H(i)7 = h + H(i-1)7
在對全部消息分組完成上述計算以後,計算最終輸出。對於SHA-256,是全部H(N)0、H(N)1到H(N)7的串聯。對於SHA-224,則是H(N)0、H(N)1直到H(N)6的串聯。
5、代碼實現
前面咱們已經說明了SHA-256(SHA-224)的計算過程,接下來咱們將這一過程代碼化。一樣的首先定義一個上下文的結構。
1 /** 定義SHA-256哈希操做的內容信息結構體 */ 2 typedef struct SHA256Context { 3 uint32_t Intermediate_Hash[SHA256HashSize/4]; /* 信息摘要 */ 4 uint32_t Length_High; /* 按位計算的信息長度高字 */ 5 uint32_t Length_Low; /* 按位計算的信息長度低字 */ 6 int_least16_t Message_Block_Index; /* 信息分組數組的索引 */ 7 uint8_t Message_Block[SHA256_Message_Block_Size];/* 512位信息分組 */ 8 int Computed; /* 摘要計算標識 */ 9 int Corrupted; /* 信息摘要損壞標識 */ 10 } SHA256Context;
接下來實現SHA256Context結構的初始化,爲後續的計算過程作準備。
1 static SHAStatusCode SHA224_256Reset(SHA256Context *context, uint32_t *H0) 2 { 3 if (!context) return shaNull; 4 context->Length_High = context->Length_Low = 0; 5 context->Message_Block_Index = 0; 6 context->Intermediate_Hash[0] = H0[0]; 7 context->Intermediate_Hash[1] = H0[1]; 8 context->Intermediate_Hash[2] = H0[2]; 9 context->Intermediate_Hash[3] = H0[3]; 10 context->Intermediate_Hash[4] = H0[4]; 11 context->Intermediate_Hash[5] = H0[5]; 12 context->Intermediate_Hash[6] = H0[6]; 13 context->Intermediate_Hash[7] = H0[7]; 14 context->Computed = 0; 15 context->Corrupted = shaSuccess; 16 return shaSuccess; 17 }
接下來實現信息分組的輸入,這個函數接受一個字節數組做爲下一個消息分組以便進行處理。
1 SHAStatusCode SHA256Input(SHA256Context *context, const uint8_t *message_array,unsigned int length) 2 { 3 if (!context) return shaNull; 4 if (!length) return shaSuccess; 5 if (!message_array) return shaNull; 6 if (context->Computed) return context->Corrupted = shaStateError; 7 if (context->Corrupted) return context->Corrupted; 8 while (length--) 9 { 10 context->Message_Block[context->Message_Block_Index++] =*message_array; 11 if ((SHA224_256AddLength(context, 8) == shaSuccess) &&(context->Message_Block_Index == SHA256_Message_Block_Size)) 12 SHA224_256ProcessMessageBlock(context); 13 message_array++; 14 } 15 return context->Corrupted; 16 }
固然還須要一個消息處理及最終摘要輸出的函數,這個函數將返回一個224位或者256位的信息摘要到調用者給定的Message_Digest數組。返回的信息摘要,第一個元素索引爲0,最後一個元素索引爲27(SHA-2244)或者31(SHA-256)。
1 static SHAStatusCode SHA224_256ResultN(SHA256Context *context,uint8_t Message_Digest[ ], int HashSize) 2 { 3 int i; 4 if (!context) return shaNull; 5 if (!Message_Digest) return shaNull; 6 if (context->Corrupted) return context->Corrupted; 7 if (!context->Computed) 8 SHA224_256Finalize(context, 0x80); 9 for (i = 0; i < HashSize; ++i) 10 Message_Digest[i] = (uint8_t)(context->Intermediate_Hash[i>>2] >> 8 * ( 3 - ( i & 0x03 ) )); 11 return shaSuccess; 12 }
至此咱們就完成了SHA-256(SHA-224)的編碼,在後續咱們將對這一編碼進行驗證。
6、結論
上一節咱們實現了SHA-256(SHA-224)的編碼,接下來咱們來對這一實現進行驗證。咱們輸入明文「abcd」並輸出結果:
同時咱們對比一下其餘工具生成的「abcd」的SHA-256的信息摘要結果以下:
對比上述兩個結果,咱們發現是徹底一致的,說明咱們的編碼是沒有問題的。
歡迎關注: