SHA-256 算法原理

SHA256 是 SHA-2 下细分出的一种算法,SHA-2下又可再分为六个不同的算法标准。

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

这些变体除了 生成摘要的长度 、循环运行的次数等一些微小差异外,算法的基本结构是一致的。

SHA256 就是一个哈希函数。哈希函数,又称散列算法,是一种从任何一种数据中创建小的数字 “指纹” 的方法。

散列函数把消息或数据压缩成摘要,使得数据量变小,将数据的格式固定下来。该函数将数据打乱混合,重新创建一个叫做散列值(或哈希值)的指纹。

散列值通常用一个短的随机字母和数字组成的字符串来代表。

对于任意长度的消息,SHA256 都会产生一个 256bit 长的哈希值,称作消息摘要。

 

1. SHA256原理

1.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而来。

在 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 而来。

1.2 信息预处理(pre-processing)

SHA256 算法中的预处理就是在想要 Hash 的消息后面补充需要的信息,使整个消息满足指定的结构。

信息的预处理分为两个步骤:附加填充比特和附加长度。

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:附加长度值

附加长度值就是将原始数据(第一步填充前的消息)的长度信息补到已经进行了填充操作的消息后面。

SHA256 用一个 64 位的数据来表示原始消息的长度。

因此,通过 SHA256 计算的消息长度必须要小于 2^64,当然绝大多数情况这足够大了。

长度信息的编码方式为 64-bit big-endian integer。

回到刚刚的例子,消息 “abc”,3个字符,占用24个bit。

因此,在进行了补长度的操作以后,整个消息就变成下面这样了(16进制格式)。

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

 1.3 计算消息摘要

现在来介绍 SHA256 算法的主体部分,即消息摘要是如何计算的。

首先:将消息分解成512-bit大小的块。

 

 

假设消息M可以被分解为n个块,于是整个算法需要做的就是完成n次迭代,n次迭代的结果就是最终的哈希值,即256bit的数字摘要。

一个256-bit的摘要的初始值H0,经过第一个数据块进行运算,得到H1,即完成了第一次迭代

H1经过第二个数据块得到H2,……,依次处理,最后得到Hn,Hn即为最终的256-bit消息摘要

图中256-bit的Hi被描述8个小块,这是因为SHA256算法中的最小运算单元称为 “字”(Word),一个字是32位。

此外,第一次迭代中,映射的初值设置为前面介绍的8个哈希初值,如下图所示:

 

下面开始介绍每一次迭代的内容

STEP1:构造64个字(word)

对于每一块,将块分解为16个32-bit的big-endian的字,记为w[0], …, w[15]

也就是说,前 16 个字直接由消息的第 i 个块分解得到

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

 

STEP2:进行64次循环

 

2.算法伪代码​

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

 

C语言实现 SHA-256 算法,可以参考:https://en.wikipedia.org/wiki/SHA-2,以下代码使用该站点的测试用例测试通过。SHA256 算法是一个哈希函数,又称散列算法,是一 ...