SHA簡介

原文鏈接:https://blog.csdn.net/u011583927/article/details/80905740

SHA256是SHA-2下細分出的一種算法

SHA-2,名稱來自於安全散列算法2(英語:Secure Hash Algorithm 2)的縮寫,一種密碼散列函數算法標準,由美國國家安全局研發,屬於SHA算法之一,是SHA-1的後繼者。

SHA-2下又可再分爲六個不同的算法標準

包括了:SHA-224、SHA-256、SHA-384、SHA-512、SHA-512/224、SHA-512/256。

這些變體除了生成摘要的長度 、循環運行的次數等一些微小差異外,算法的基本結構是一致的。

回到SHA256上,說白了,它就是一個哈希函數。

哈希函數,又稱散列算法,是一種從任何一種數據中創建小的數字“指紋”的方法。散列函數把消息或數據壓縮成摘要,使得數據量變小,將數據的格式固定下來。該函數將數據打亂混合,重新創建一個叫做散列值(或哈希值)的指紋。散列值通常用一個短的隨機字母和數字組成的字符串來代表。

對於任意長度的消息,SHA256都會產生一個256bit長的哈希值,稱作消息摘要

這個摘要相當於是個長度爲32個字節的數組,通常用一個長度爲64的十六進制字符串來表示

來看一個例子:

幹他100天成爲區塊鏈程序員,紅軍大叔帶領着我們,fighting!

這句話,經過哈希函數SHA256後得到的哈希值爲:

A7FCFC6B5269BDCCE571798D618EA219A68B96CB87A0E21080C2E758D23E4CE9

可以在網上找一個SHA256在線驗證工具,可以用來進行SHA256哈希結果的驗證,後面也可以用來檢驗自己的SHA256代碼是否正確。用起來很方便,不妨感受下

2. SHA256原理詳解

爲了更好的理解SHA256的原理,這裏首先將算法中可以單獨抽出的模塊,包括常量的初始化信息預處理使用到的邏輯運算分別進行介紹,甩開這些理解上的障礙後,一起來探索SHA256算法的主體部分,即消息摘要是如何計算的

2.1 常量初始化

SHA256算法中用到了8個哈希初值以及64個哈希常量

其中,SHA256算法的8個哈希初值如下:

h0 := 0x6a09e667
h1 := 0xbb67ae85
h2 := 0x3c6ef372
h3 := 0xa54ff53a
h4 := 0x510e527f
h5 := 0x9b05688c
h6 := 0x1f83d9ab
h7 := 0x5be0cd19

這些初值是對自然數中前8個質數(2,3,5,7,11,13,17,19)的平方根的小數部分取前32bit而來

舉個例子來說,$ \sqrt{2} $小數部分約爲0.414213562373095048,而
0.414213562373095048≈6∗16−1+a∗16−2+0∗16−3+...

0.414213562373095048≈6∗16−1+a∗16−2+0∗16−3+...
於是,質數2的平方根的小數部分取前32bit就對應出了0x6a09e667

在SHA256算法中,用到的64個常量如下:

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

和8個哈希初值類似,這些常量是對自然數中前64個質數(2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97…)的立方根的小數部分取前32bit而來。

STEP1:附加填充比特

在報文末尾進行填充,使報文長度在對512取模以後的餘數是448

填充是這樣進行的:先補第一個比特爲1,然後都補0,直到長度滿足對512取模後餘數是448。

需要注意的是,信息必須進行填充,也就是說,即使長度已經滿足對512取模後餘數是448,補位也必須要進行,這時要填充512個比特。

因此,填充是至少補一位,最多補512位

例:以信息“abc”爲例顯示補位的過程。

a,b,c對應的ASCII碼分別是97,98,99

於是原始信息的二進制編碼爲:01100001 01100010 01100011

補位第一步,首先補一個“1” : 0110000101100010 01100011 1

補位第二步,補423個“0”:01100001 01100010 01100011 10000000 00000000 … 00000000

補位完成後的數據如下(爲了簡介用16進製表示):

61626380 00000000 00000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000

爲什麼是448?

因爲在第一步的預處理後,第二步會再附加上一個64bit的數據,用來表示原始報文的長度信息。而448+64=512,正好拼成了一個完整的結構。

STEP2:附加長度值

附加長度值就是將原始數據(第一步填充前的消息)的長度信息補到已經進行了填充操作的消息後面。

wiki百科中給出的原文是:append length of message (before pre-processing), in bits, as 64-bit big-endian integer

SHA256用一個64位的數據來表示原始消息的長度。

因此,通過SHA256計算的消息長度必須要小於$ 2^64 $,當然絕大多數情況這足夠大了。

長度信息的編碼方式爲64-bit big-endian integer

關於Big endian的含義,文末給出了補充

回到剛剛的例子,消息“abc”,3個字符,佔用24個bit

因此,在進行了補長度的操作以後,整個消息就變成下面這樣了(16進制格式)

61626380 00000000 00000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000018

2.3 邏輯運算

SHA256散列函數中涉及的操作全部是邏輯的位運算

包括如下的邏輯函數:

Ch(x,y,z)=(x∧y)⊕(¬x∧z)

Ch(x,y,z)=(x∧y)⊕(¬x∧z)
Ma(x,y,z)=(x∧y)⊕(x∧z)⊕(y∧z)Ma(x,y,z)=(x∧y)⊕(x∧z)⊕(y∧z)
Σ0(x)=S2(x)⊕S13(x)⊕S22(x)Σ0​(x)=S2(x)⊕S13(x)⊕S22(x)
Σ1(x)=S6(x)⊕S11(x)⊕S25(x)Σ1​(x)=S6(x)⊕S11(x)⊕S25(x)
σ0(x)=S7(x)⊕S18(x)⊕R3(x)σ0​(x)=S7(x)⊕S18(x)⊕R3(x)
σ1(x)=S17(x)⊕S19(x)⊕R10(x)

σ1​(x)=S17(x)⊕S19(x)⊕R10(x)
其中:

邏輯運算 含義
按位“與”
¬
¬ 按位“補”
按位“異或”
Sn
Sn 循環右移n個bit
Rn
Rn

右移n個bit

 

2.4 計算消息摘要

現在來介紹SHA256算法的主體部分,即消息摘要是如何計算的。

首先:將消息分解成512-bit大小的塊

(break message into 512-bit chunks)

 

假設消息M可以被分解爲n個塊,於是整個算法需要做的就是完成n次迭代,n次迭代的結果就是最終的哈希值,即256bit的數字摘要。

一個256-bit的摘要的初始值H0,經過第一個數據塊進行運算,得到H1,即完成了第一次迭代

H1經過第二個數據塊得到H2,……,依次處理,最後得到Hn,Hn即爲最終的256-bit消息摘要

將每次迭代進行的映射用$ Map(H_{i-1}) = H_{i} $表示,於是迭代可以更形象的展示爲:

圖中256-bit的Hi被描述8個小塊,這是因爲SHA256算法中的最小運算單元稱爲“字”(Word),一個字是32位。

此外,第一次迭代中,映射的初值設置爲前面介紹的8個哈希初值,如下圖所示:

圖::哈希初值3

STEP1:構造64個字(word)

break chunk into sixteen 32-bit big-endian words w[0], …, w[15]

對於每一塊,將塊分解爲16個32-bit的big-endian的字,記爲w[0], …, w[15]

也就是說,前16個字直接由消息的第i個塊分解得到

其餘的字由如下迭代公式得到:

Wt=σ1(Wt−2)+Wt−7+σ0(Wt−15)+Wt−16
Wt​=σ1​(Wt−2​)+Wt−7​+σ0​(Wt−15​)+Wt−16​

STEP2:進行64次循環

映射 $ Map(H_{i-1}) = H_{i} $ 包含了64次加密循環

即進行64次加密循環即可完成一次迭代

每次加密循環可以由下圖描述:

圖中,ABCDEFGH這8個字(word)在按照一定的規則進行更新,其中

深藍色方塊是事先定義好的非線性邏輯函數,上文已經做過鋪墊

紅色田字方塊代表 mod $ 2^{32} $ addition,即將兩個數字加在一起,如果結果大於$ 2^{32} ,你必須除以

,你必須除以 2^{32} $並找到餘數。

ABCDEFGH一開始的初始值分別爲$ H_{i-1}(0),H_{i-1}(1),…,H_{i-1}(7) $

Kt是第t個密鑰,對應我們上文提到的64個常量

Wt是本區塊產生第t個word。原消息被切成固定長度512-bit的區塊,對每一個區塊,產生64個word,通過重複運行循環n次對ABCDEFGH這八個字循環加密。

最後一次循環所產生的八個字合起來即是第i個塊對應到的散列字符串$ H_{i} $

由此變完成了SHA256算法的所有介紹

3. SHA256算法僞代碼

現在我們可以結合SHA256算法的僞代碼,將上述的所有步驟進行梳理整合:

Note: All variables are unsigned 32 bits and wrap modulo 232 when calculating


Initialize variables
(first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19):
h0 := 0x6a09e667
h1 := 0xbb67ae85
h2 := 0x3c6ef372
h3 := 0xa54ff53a
h4 := 0x510e527f
h5 := 0x9b05688c
h6 := 0x1f83d9ab
h7 := 0x5be0cd19


Initialize table of round constants
(first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311):
k[0..63] :=
   0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
   0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
   0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
   0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
   0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
   0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
   0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
   0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2


Pre-processing:
append the bit '1' to the message
append k bits '0', where k is the minimum number >= 0 such that the resulting message
    length (in bits) is congruent to 448(mod 512)
append length of message (before pre-processing), in bits, as 64-bit big-endian integer


Process the message in successive 512-bit chunks:
break message into 512-bit chunks
for each chunk
    break chunk into sixteen 32-bit big-endian words w[0..15]

    Extend the sixteen 32-bit words into sixty-four 32-bit words:
    for i from 16 to 63
        s0 := (w[i-15] rightrotate 7) xor (w[i-15] rightrotate 18) xor(w[i-15] rightshift 3)
        s1 := (w[i-2] rightrotate 17) xor (w[i-2] rightrotate 19) xor(w[i-2] rightshift 10)
        w[i] := w[i-16] + s0 + w[i-7] + s1

    Initialize hash value for this chunk:
    a := h0
    b := h1
    c := h2
    d := h3
    e := h4
    f := h5
    g := h6
    h := h7

    Main loop:
    for i from 0 to 63
        s0 := (a rightrotate 2) xor (a rightrotate 13) xor(a rightrotate 22)
        maj := (a and b) xor (a and c) xor(b and c)
        t2 := s0 + maj
        s1 := (e rightrotate 6) xor (e rightrotate 11) xor(e rightrotate 25)
        ch := (e and f) xor ((not e) and g)
        t1 := h + s1 + ch + k[i] + w[i]
        h := g
        g := f
        f := e
        e := d + t1
        d := c
        c := b
        b := a
        a := t1 + t2

    Add this chunk's hash to result so far:
    h0 := h0 + a
    h1 := h1 + b
    h2 := h2 + c
    h3 := h3 + d
    h4 := h4 + e
    h5 := h5 + f
    h6 := h6 + g
    h7 := h7 + h

Produce the final hash value (big-endian):
digest = hash = h0 append h1 append h2 append h3 append h4 append h5 append h6 append h7

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章