SHA256算法详解及python实现
1.SHA256介紹(可略過)
SHA256是SHA-2下細分出的一種算法。SHA-2(安全哈希算法2)是由美國國家安全局(NSA)設計的一組加密哈希函數。SHA-2系列由六個具有224、256、384或512位摘要(哈希值)的哈希函數組成:SHA-224,SHA-256,SHA-384,SHA-512,SHA-512 / 224,SHA -512/256。SHA-256和SHA-512是分別用32位和64位字計算的哈希函數。它們使用不同的移位量和加性常數,但是它們的結構實際上是相同的,只是輪數不同。
SHA256其實就是一個哈希函數。哈希函數,又稱散列算法,是一種從任何一種數據中創建小的數字“指紋”的方法。散列函數把消息或數據壓縮成摘要,使得數據量變小,將數據的格式固定下來。該函數將數據打亂混合,重新創建一個叫做散列值(或哈希值)的指紋。散列值通常用一個短的隨機字母和數字組成的字符串來代表。
對于任意長度的消息,SHA256都會產生一個256bit長的哈希值,稱作消息摘要。這個摘要相當于是個長度為32個字節的數組,通常用一個長度為64的十六進制字符串來表示。
2. SHA算法詳解
2.1常量初始化
SHA256算法中用到了8個哈希初值以及64個哈希常量。
SHA256算法的8個哈希初值為前8個質數(2,3,5,7,11,13,17,19)的平方根的小數部分取前32bit而來,如下所示(8個哈希值為圖中的1所框出的部分,即第一次迭代的(A,B,…,H)值):
#對于2\sqrt22?≈0.414213562373095048,而0.414213562373095048≈6×16?1+a×16?2+0×16?3+9×16?4+e×16?5+6×16?6+6×16?7+7×16?86×16^{-1}+a×16^{-2}+0×16^{-3}+9×16^{-4}+e×16^{-5}+6×16^{-6}+6×16^{-7}+7×16^{-8}6×16?1+a×16?2+0×16?3+9×16?4+e×16?5+6×16?6+6×16?7+7×16?8。故質數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這些常量是對自然數中前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而來(圖中64次迭代的Kt值)。
2.2預處理
SHA256對進入的信息要進行初始化,即使消息滿足指定結構。信息處理主要是消息填充和附加長度。(1.通過在消息后增加0,使消息達到指定長度。2.并在最后增加消息長度信息)
(1)在報文末尾進行填充,使報文長度在對512取模以后的余數是448。
填充是這樣進行的:先補第一個比特為1,然后都補0,直到長度滿足對512取模后余數是448。信息必須進行填充,也就是說,即使長度已經滿足對512取模后余數是448,補位也必須要進行,這時要填充512個比特。(即最少補一位,最多補512位)。
(2)這里余數是448的原因是填充后,會再附加上一個64bit的數據,用來表示原始報文的長度信息。而448+64=512,正好拼成了一個完整的結構。附加的64bit的數據,即用一個64位的數據表示原始消息的長度(故消息長度必須小于2^64)。長度信息的編碼方式為64-bit big-endian integer(可以簡單地理解成,從最高位開始數數字長度)。
2.3邏輯運算
| ∧ | 按位“與” |
| ? | 按位“補” |
| ⊕ | 按位“異或” |
| Sn\ S^{n}?Sn | 循環右移n個bit |
| Rn\ R^{n}?Rn | 右移n個bit |
涉及的邏輯函數:
Ch(x,y,z)=(x∧y)⊕(?x∧z)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)Ma(x,y,z)=(x∧y)⊕(x∧z)⊕(y∧z)
Σ0(x)=S2(x)⊕S13(x)⊕S22(x)Σ0(x)=S^2 (x)⊕S^13 (x)⊕S^22 (x)Σ0(x)=S2(x)⊕S13(x)⊕S22(x)
Σ1(x)=S6(x)⊕S11(x)⊕S25(x)Σ1(x)=S^6 (x)⊕S^11 (x)⊕S^25 (x)Σ1(x)=S6(x)⊕S11(x)⊕S25(x)
σ0(x)=S7(x)⊕S18(x)⊕S3(x)σ0(x)=S^7 (x)⊕S^18 (x)⊕S^3 (x)σ0(x)=S7(x)⊕S18(x)⊕S3(x)
σ1(x)=S17(x)⊕S19(x)⊕S10(x)σ1(x)=S^17 (x)⊕S^19 (x)⊕S^10 (x)σ1(x)=S17(x)⊕S19(x)⊕S10(x)
田(x,y)=(x+y)mod232田(x,y)=(x+y)mod 2^{32}田(x,y)=(x+y)mod232
2.4計算消息摘要
(1)將消息分解成512bit的若干塊。此處若是消息被分成n塊,則整個代碼就要完成n個64迭代(圖中的一次運算為1個迭代,每個塊要經過64次迭代)。
(2)構造64個字,對于消息分解成的每個512bit的塊,需要構成64個字(每個字節是8位二進制,每個字有4個字節,故每個字有32bit)。前16個字直接由原消息組成,記為W[0] 、…W[15]。對于其余字則有迭代公式計算而得:
Wt=σ1(Wt?2)+Wt?7+σ0(W(t?15))+Wt?16W_t=σ1(W_{t-2} )+W_{t-7}+σ0(W_(t-15) )+W_{t-16}Wt?=σ1(Wt?2?)+Wt?7?+σ0(W(?t?15))+Wt?16?
(3)進行64次加密循環。
第一次迭代時,A、B、…H是8個哈希初始值(見2.1)。
在(a)處:(a)=田(Wt,Kt)\ 田(W_t,K_t )?田(Wt?,Kt?)。(Wt\ W_t?Wt?為2.4(2)中構造的字,Kt\ K_t?Kt?為2.1中的哈希常量)。
在(b)處:(b)= 田(Ch(E,F,G), H, (a))。
在(c)處:(c)= 田(Σ1(E), (b))。
在(d)處:(d)= 田(D,(c))。
在(e)處:(e)= 田(Ma(A,B,C),(c))。
在(f)處:(f)= 田(Σ0(A),(e))。
一次循環可計算得:Bi=A,Ci=B,Di=C,Ei=(d),Fi=E,Gi=F,Hi=G,Ai=(f)。
經過64次迭代,可以得到第一個512塊函數的摘要。
對于下一個512bit的塊,將壓縮塊添加到當前哈希值:h0:= h0 + Ai;h1:= h1 + Bi;h2:= h2 + Ci;h3:= h3 + Di;h4:= h4 + Ei;h5:= h5 + Fi;h6:= h6 + Gi;h7:= h7 + Hi。
(h0,…,h7初始值見2.1)
最后的計算的SHA256的值為:digest= h0 append h1 append h2 append h3 append h4 append h5 append h6 append h7。(將h0,…h7連接起來)
3. SHA256偽代碼
(同wiki)
Note 1: All variables are 32 bit unsigned integers and addition is calculated modulo 232 Note 2: For each round, there is one round constant k[i] and one entry in the message schedule array w[i], 0 ≤ i ≤ 63 Note 3: The compression function uses 8 working variables, a through h Note 4: Big-endian convention is used when expressing the constants in this pseudocode,and when parsing message block data from bytes to words, for example,the first word of the input message "abc" after padding is 0x61626380Initialize hash values: (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 := 0x5be0cd19Initialize array 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, 0xc67178f2Pre-processing (Padding): begin with the original message of length L bits append a single '1' bit append K '0' bits, where K is the minimum number >= 0 such that L + 1 + K + 64 is a multiple of 512 append L as a 64-bit big-endian integer, making the total post-processed length a multiple of 512 bitsProcess the message in successive 512-bit chunks: break message into 512-bit chunks for each chunkcreate a 64-entry message schedule array w[0..63] of 32-bit words(The initial values in w[0..63] don't matter, so many implementations zero them here)copy chunk into first 16 words w[0..15] of the message schedule arrayExtend the first 16 words into the remaining 48 words w[16..63] of the message schedule array:for i from 16 to 63s0 := (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] + s1Initialize working variables to current hash value:a := h0b := h1c := h2d := h3e := h4f := h5g := h6h := h7Compression function main loop:for i from 0 to 63S1 := (e rightrotate 6) xor (e rightrotate 11) xor (e rightrotate 25)ch := (e and f) xor ((not e) and g)temp1 := h + S1 + ch + k[i] + w[i]S0 := (a rightrotate 2) xor (a rightrotate 13) xor (a rightrotate 22)maj := (a and b) xor (a and c) xor (b and c)temp2 := S0 + majh := gg := ff := ee := d + temp1d := cc := bb := aa := temp1 + temp2Add the compressed chunk to the current hash value:h0 := h0 + ah1 := h1 + bh2 := h2 + ch3 := h3 + dh4 := h4 + eh5 := h5 + fh6 := h6 + gh7 := h7 + hProduce the final hash value (big-endian): digest := hash := h0 append h1 append h2 append h3 append h4 append h5 append h6 append h74. python實現代碼
github鏈接
class SHA256:def __init__(self):#64個常量#圖中Ktself.constants = (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)#迭代初始值,h0,h1,...,h7self.h = (0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19)#x循環右移b個bit#rightrotate b bitdef rightrotate(self, x, b):return ((x >> b) | (x << (32 - b))) & ((2**32)-1)#信息預處理。附加填充和附加長度值def Pad(self, W):return bytes(W, "ascii") + b"\x80" + (b"\x00" * ((55 if (len(W) % 64) < 56 else 119) - (len(W) % 64))) + ((len(W) << 3).to_bytes(8, "big"))def Compress(self, Wt, Kt, A, B, C, D, E, F, G, H):return ((H + (self.rightrotate(E, 6) ^ self.rightrotate(E, 11) ^ self.rightrotate(E, 25)) + ((E & F) ^ (~E & G)) + Wt + Kt) + (self.rightrotate(A, 2) ^ self.rightrotate(A, 13) ^ self.rightrotate(A, 22)) + ((A & B) ^ (A & C) ^ (B & C))) & ((2**32)-1), A, B, C, (D + (H + (self.rightrotate(E, 6) ^ self.rightrotate(E, 11) ^ self.rightrotate(E, 25)) + ((E & F) ^ (~E & G)) + Wt + Kt)) & ((2**32)-1), E, F, Gdef hash(self, message):message = self.Pad(message)digest = list(self.h)for i in range(0, len(message), 64):S = message[i: i + 64]W = [int.from_bytes(S[e: e + 4], "big") for e in range(0, 64, 4)] + ([0] * 48)#構造64個wordfor j in range(16, 64):W[j] = (W[j - 16] + (self.rightrotate(W[j - 15], 7) ^ self.rightrotate(W[j - 15], 18) ^ (W[j - 15] >> 3)) + W[j - 7] + (self.rightrotate(W[j - 2], 17) ^ self.rightrotate(W[j - 2], 19) ^ (W[j - 2] >> 10))) & ((2**32)-1)A, B, C, D, E, F, G, H = digestfor j in range(64):A, B, C, D, E, F, G, H = self.Compress(W[j], self.constants[j], A, B, C, D, E, F, G, H)return "".join(format(h, "02x") for h in b"".join(d.to_bytes(4, "big") for d in [(x + y) & ((2**32)-1) for x, y in zip(digest, (A, B, C, D, E, F, G, H))]))def main():encoder = SHA256()while True:message = input("Enter string: ")print(f"Output: {encoder.hash(message)}\n")if __name__ == "__main__":main()參考文獻
SHA-2 wiki
總結
以上是生活随笔為你收集整理的SHA256算法详解及python实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 断言NSAssert的使用
- 下一篇: 【剑指offer】——【python中r