Sha散列家族

Sha家族离散

SHA-1

流程概况

对于任意长度的明文,SHA1首先对其进行分组,使得每一组的长度为512位,然后对这些明文分组反复重复处理。对于每个明文分组的摘要生成过程如下:

  1. 将512位的明文分组划分为16个子明文分组,每个子明文分组为32位。

  2. 申请5个32位的链接变量,记为A、B、C、D、E。

  3. 16份子明文分组扩展为80份。

  4. 80份子明文分组进行4轮运算。

  5. 链接变量与初始链接变量进行求和运算。

  6. 链接变量作为下一个明文分组的输入重复进行以上操作。

  7. 最后,5个链接变量里面的数据就是SHA1摘要。

1. 分组

对任意长度的明文,先是先将明文填充到448(mod512)位,然后再将真正的明文的长度(未填充到448位之前的)以64位表示,附加在以填充到448位的明文之后。

与MD5不同的是,SHA1的原始报长不能超过\(2^{64}\),而且SHA1的明文长度从低位开始填充。

差异处 MD5 SHA1
摘要长度 128位 160位
运算步骤数 64 80
基本逻辑函数数目 4 4
常数数目 64 4

对于512位的明文分组,SHA1将其再分成16份子明文分组(sub-block),每份子明文分组为32位,我们使用M[k](k= 0, 1,……15)来表示这16份子明文分组。之后还要将这16份子明文分组扩充到80份子明文分组,我们记为W[k](k= 0, 1,……79),扩充的方法如下。 \[ W _t = M_t \ 当0\leq t\leq 15\\ W_t = (W_{t-3} \oplus W_{t-8} \oplus W_{t-14} \oplus W_{t-16}) <<< \ 1\\ [<<<] \rightarrow 循环左移 \] SHA1有4轮运算,每一轮包括20个步骤(一共80步),最后产生160位摘要,这160位摘要存放在5个32位的链接变量中,分别标记为A、B、C、D、E。这5个链接变量的初始值以16进制位表示如下。

A=0x67452301 B=0xEFCDAB89 C=0x98BADCFE D=0x10325476 E=0xC3D2E1F0

2. 4轮运算

SHA1有4轮运算,每一轮包括20个步骤,一共80步,当第1轮运算中的第1步骤开始处理时,A、B、C、D、E五个链接变量中的值先赋值到另外5个记录单元A′,B′,C′,D′,E′中。这5个值将保留,用于在第4轮的最后一个步骤完成之后与链接变量A,B,C,D,E进行求和操作。

SHA1的4轮运算,共80个步骤使用同一个操作程序,如下:

\[ A,B,C,D,E←[(A<<<5)+ ft(B,C,D)+E+W_t+K_t],A,(B<<<30),C,D\\ 其中 ft(B,C,D)为逻辑函数,W_t为子明文分组W[t],K_t为固定常数。 \] 这个操作程序的意义为:

● 将\([(A<<<5)+ ft(B,C,D)+E+W_t+K_t]\)的结果赋值给链接变量A;

● 将链接变量A初始值赋值给链接变量B;

● 将链接变量B初始值循环左移30位赋值给链接变量C;

● 将链接变量C初始值赋值给链接变量D;

● 将链接变量D初始值赋值给链接变量E。

\(ft(B,C,D)\):

步骤 函数定义
1 0≤t≤19 \(ft(B,C,D)=(B\ and\ C)\ or\ ((not B)\ and\ D)\)
2 20≤t≤39 \(ft(B,C,D)=B⊕C⊕D\)
3 40≤t≤59 \(ft(B,C,D)=(B\ and\ C)\ or\ (B\ and\ D)\ or\ (C\ and\ D)\)
4 60≤t≤79 \(ft(B,C,D)=B⊕C⊕D\)

在操作程序中需要使用固定常数\(K_i\)(i= 0,1,2,……79)

步骤 \(K_t\)
1 0≤t≤19 5A827999
2 20≤t≤39 6ED9EBA1
3 40≤t≤59 8F188CDC
4 60≤t≤79 CA62C1D6

3. 添加

\[ h_0 = A + A^′,h_1 = B + B^′,h_2 = C + C^′,h_3 = D + D^′,h_4 = E + E^′ \]

\(h_0\)~\(h_4\)作为输入成为下一个512位明文分组的链接变量A,B,C,D,E。当最后一个明文分组计算完成以后

1
result = (A << 32*4) | (B << 32*3) | (C << 32*2) | (D << 32) | E

SHA-2

SHA-2可再分为六个不同的算法标准,包括了:SHA-224、SHA-256、SHA-384、SHA-512、SHA-512/224、SHA-512/256。

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

SHA-256

概述

总体上,HSA256与MD4、MD5以及HSA-1等哈希函数的操作流程类似,待哈希的消息在继续哈希计算之前首先要进行以下两个步骤:

  • 对消息进行补位处理,是的最终的长度是512位的倍数,然后

  • 以512位为单位对消息进行分块为\(M^{(1)},M^{(2)},...,M^{(N)}\)

消息区块将进行逐个处理:从一个固定的初始哈希\(H^{(0)}\)开始,进行以下序列的计算: \[ H^{(i)} = H^{(i-1)}+C_{M^{(i-1)}}(H^{(i-1)}) \] 其中\(C\)是SHA256的压缩函数,\(+\)\(mod\ 2^{32}\)加法,即将两个数字加在一起,如果对\(2^{32}\)取余, \(H^{(N)}\)是消息区块的哈希值

详细描述

SHA256的压缩函数主要对512位的消息区块和256位的中间哈希值进行操作,本质上,它是一个通过将消息区块为密钥对中间哈希值进行加密的256位加密算法。 因此,为了描述SHA256算法,有以下两方面的组件需要描述:

  • SHA256压缩函数
  • SHA256消息处理流程

以下的描述当中所使用到的标记如下:

  • ⊕: 按位异或
  • \(\wedge\): 按位与
  • \(\vee\): 按位或
  • ¬: 补位
  • \(+\): 相加以后对\(2^{32}\)求余
  • \(R^n\): 右移n位
  • \(S^n\): 循环右移n位

以上所有的操作都是针对32位字节.

常量初始化

初始哈希值\(H^{(0)}\)取自自然数中前面8个素数(2,3,5,7,11,13,17,19)的平方根的小数部分, 并且取前面的32位.

于是, 质数2的平方根的小数部分取前32位就对应0x6a09e667.

如此类推, 初始哈希值由以下8个32位的哈希初值构成: $ H_1^{(0)}=6a09e667 $ $ H_2^{(0)}=bb67ae85 $ $ H_3^{(0)}=3c6ef372 $ $ H_4^{(0)}=a54ff53a $ $ H_5^{(0)}=510e527f $ $ H_6^{(0)}=9b05688c $ $ H_7^{(0)}=1f83d9ab $ $ H_8^{(0)}=5be0cd19 $ SHA256算法当中还使用到64个常数, 取自自然数中前面64个素数的立方根的小数部分的前32位, 如果用16进制表示, 则相应的常数序列如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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]

消息补全

和SHA-1相同的补全方法。

  • 将补码处理后的消息以512位为单位分块为: \(M^{(1)},M^{(2)},...,M^{(N)}\), 其中第\(i\)个消息块的前32位表示为: \(M_0^{(i)}\), 后面32位为: \(M_1^{(i)}\), 以此类推, 最后32位的消息块可表示为: \(M_{15}^{(i)}\). 我们采用Big endian约定对数据进行编码, 即认为第一个字节是最高位字节, 因此, 对于每一个32位字节, 最最左边的比特是最大的比特位.

摘要计算主循环

哈希计算算法如下:

  • \(For\ i = i\rightarrow N\)(N=是补全后消息块个数)

    • 用第\((i-1)\)个中间哈希值来对\(a,b,c,d,e,f,g,h\)进行初始化,当\(i=1\)时,就初始化哈希,即 $ a H_1^{(i-1)} $ $ b H_2^{(i-1)} $ $ $ $ h H_8^{(i-1)} $

    • 应用SHA256压缩函数来更新\(a,b,c,d,e,f,g,h\)

      • \(Forj = 0 \rightarrow 63\)

        • 计算\(Ch(e,f,g),M_{aj}(a,b,c),\Sigma_0(a),\Sigma_1(e),W_j\) \(T_1 \leftarrow h +\Sigma_1(e)+Ch(e,f,g)+K_j+W_j\) $ T_2 0(a)+M{aj}(a,b,c) $ $ h g $ $ g f $ $ f e $ $ e d+T_1 $ $ d c $ $ c b $ $ b a $ $ a T_1 + T_2 $
    • 计算第\(i\)个中间哈希值\(H^{(i)}\) $ H_1{(i-1)} a+H_1{(i-1)} $ $ H_2{(i-1)} b+H_2{(i-1)} $ $ $ $ H_8{(i-1)} h+H_8{(i-1)} $

  • \(H^{(N)}=(H_1^{(N)},H_2^{(N)},...,H_8^{(N)})\)为最终的哈希值\(M\);

逻辑函数定义

SHA256算法当中所使用到的6个逻辑函数如下:每个函数都对32位字节进行操纵,并输出32位字节。 $ Ch(x,y,z)=(xy)(¬xz)\[ M_{aj}(x,y,z) = (x\wedge y)\oplus(x\wedge z)\oplus(y\wedge z)\] _0(x) = S2(x)S{13}(x)S^{22}(x)\[ \Sigma_1(x) = S^6(x)\oplus S^{11}(x)\oplus S^{25}(x)\] _0(x) = S7(x)S{18}(x)R^{3}(x)$$ _1(x) = S{17}(x)S{19}(x)R^{10}(x) $ 扩展消息块\(W_0,W_1,...,W_{63}\)通过以下方式进行计算:

  • \(W_j = M_j^{(i)}\ forj = 0,1,2,..,15\)

  • \(Forj = 16 \rightarrow 63\)

    • \(W_j \leftarrow \sigma_1(W_{j-2})+W_{j-7}+\sigma_0(W_{j-15})+W_{j-16}\)

图像表示

SHA256压缩函数的图形表示如下:

img

扩展消息块\(W_j\)的求解算法可以表示如下:

img

伪代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
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 0x61626380

Initialize 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 := 0x5be0cd19

Initialize 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, 0xc67178f2

Pre-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 bits

Process the message in successive 512-bit chunks:
break message into 512-bit chunks
for each chunk
create 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 array

Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array:
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 working variables to current hash value:
a := h0
b := h1
c := h2
d := h3
e := h4
f := h5
g := h6
h := h7

Compression function main loop:
for i from 0 to 63
S1 := (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 + maj

h := g
g := f
f := e
e := d + temp1
d := c
c := b
b := a
a := temp1 + temp2

Add the compressed chunk to the current hash value:
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

代码实现

1)sha256.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/*
* Copyright (c)
* Filename: sha256.h
* Brief: SHA256算法实现
* Depend: C++11
*
* Version: V1.0.0
* Date: 2019/11/08-2019/11/13
* Author: LucianY
* Note: 初次版本。
*
* Version: V2.0.0
* Date: 2023/01/04
* Author: LucianY
* Note: 1、代码优化:使用单例模式;
* 2、debug:支持加密空字符串;
* 3、性能优化。
*
* Attention: 输入信息中有中文时,得到的数字指纹与使用其他工具得到数字指纹可能不相同。原因是不同平台中文的编码方式不同。
*/

#ifndef LY_SHA256_H
#define LY_SHA256_H

#include <cstdint>

#include <string>
#include <vector>

namespace Ly {

/**
* @brief SHA256加密类
*/
class Sha256 {
public:
//! 获取单例
inline static Sha256 &getInstance()
{
static Sha256 instance;
return instance;
}

/**
* @brief: 使用SHA256算法,加密输入信息(获取数字指纹)
* @param[in] message: 输入信息
* @return: 摘要(数字指纹)
*/
std::vector<uint8_t> encrypt(std::vector<uint8_t> message) const;

/**
* @brief: 获取十六进制表示的信息摘要(数字指纹)
* @param[in] message: 输入信息
* @return: 十六进制表示的信息摘要(数字指纹)
*/
std::string getHexMessageDigest(const std::string &message) const;

protected:
/// SHA256算法中定义的6种逻辑运算 ///

inline uint32_t ch(uint32_t x, uint32_t y, uint32_t z) const noexcept
{
return (x & y) ^ ((~x) & z);
}

inline uint32_t maj(uint32_t x, uint32_t y, uint32_t z) const noexcept
{
return (x & y) ^ (x & z) ^ (y & z);
}

inline uint32_t bigSigma0(uint32_t x) const noexcept
{
return (x >> 2 | x << 30) ^ (x >> 13 | x << 19) ^ (x >> 22 | x << 10);
}

inline uint32_t bigSigma1(uint32_t x) const noexcept
{
return (x >> 6 | x << 26) ^ (x >> 11 | x << 21) ^ (x >> 25 | x << 7);
}

inline uint32_t smallSigma0(uint32_t x) const noexcept
{
return (x >> 7 | x << 25) ^ (x >> 18 | x << 14) ^ (x >> 3);
}

inline uint32_t smallSigma1(uint32_t x) const noexcept
{
return (x >> 17 | x << 15) ^ (x >> 19 | x << 13) ^ (x >> 10);
}

/**
* @brief: SHA256算法对输入信息的预处理,包括“附加填充比特”和“附加长度值”
附加填充比特: 在报文末尾进行填充,先补第一个比特为1,然后都补0,直到长度满足对512取模后余数是448。需要注意的是,信息必须进行填充。
附加长度值: 用一个64位的数据来表示原始消息(填充前的消息)的长度,并将其补到已经进行了填充操作的消息后面。
* @param[in][out] message: 待处理的信息
*/
void preprocessing(std::vector<uint8_t> &message) const;

/**
* @brief: 将信息分解成连续的64Byte大小的数据块
* @param[in] message: 输入信息,长度为64Byte的倍数
* @return: 输出数据块
*/
std::vector<std::vector<uint8_t>> breakTextInto64ByteChunks(const std::vector<uint8_t> &message) const;

/**
* @brief: 由64Byte大小的数据块,构造出64个4Byte大小的字。
构造算法:前16个字直接由数据块分解得到,其余的字由如下迭代公式得到:
W[t] = smallSigma1(W[t-2]) + W[t-7] + smallSigma0(W[t-15]) + W[t-16]
* @param[in] chunk: 输入数据块,大小为64Byte
* @return: 输出字
*/
std::vector<uint32_t> structureWords(const std::vector<uint8_t> &chunk) const;

/**
* @breif: 基于64个4Byte大小的字,进行64次循环加密
* @param[in] words: 64个4Byte大小的字
* @param[in][out] message_digest: 信息摘要
*/
void transform(const std::vector<uint32_t> &words, std::vector<uint32_t> &message_digest) const;

/**
* @brief: 输出最终的哈希值(数字指纹)
* @param[in] input: 步长为32bit的哈希值
* @return: 步长为8bit的哈希值
*/
std::vector<uint8_t> produceFinalHashValue(const std::vector<uint32_t> &input) const;

private:
/* 单例模式 */
Sha256() = default;

Sha256(const Sha256 &) = delete;
Sha256 &operator=(const Sha256 &) = delete;

Sha256(Sha256 &&) = delete;
Sha256 &operator=(Sha256 &&) = delete;

~Sha256() = default;


// 在SHA256算法中的初始信息摘要,这些常量是对自然数中前8个质数的平方根的小数部分取前32bit而来
static const std::vector<uint32_t> initial_message_digest_;

// 在SHA256算法中,用到64个常量,这些常量是对自然数中前64个质数的立方根的小数部分取前32bit而来
static const std::vector<uint32_t> add_constant_;
};

} // namespace Ly
#endif // LY_SHA256_H

2)sha256.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/*
* Copyright (c)
* Filename: sha256.cpp
* Brief: SHA256算法实现
* Depend: C++11
*
* Version: V1.0.0
* Date: 2019/11/08-2019/11/13
* Author: LucianY
* Note: 初次版本。
*
* Version: V2.0.0
* Date: 2023/01/04
* Author: LucianY
* Note: 1、代码优化:使用单例模式;
* 2、debug:支持加密空字符串;
* 3、性能优化。
*
* Attention: 输入信息中有中文时,得到的数字指纹与使用其他工具得到数字指纹可能不相同。原因是不同平台中文的编码方式不同。
*/

#include "sha256.h"

#include <iomanip>
#include <sstream>
#include <string>
#include <vector>

namespace Ly {

const std::vector<uint32_t> Sha256::initial_message_digest_{
0x6a09e667, 0xbb67ae85, 0x3c6ef372,
0xa54ff53a, 0x510e527f, 0x9b05688c,
0x1f83d9ab, 0x5be0cd19
};

const std::vector<uint32_t> Sha256::add_constant_{
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
};

std::vector<uint8_t> Sha256::encrypt(std::vector<uint8_t> input_message) const
{
//! 文本预处理
preprocessing(input_message);

//! 将文本分解成连续的64Byte大小的数据块
auto chunks = breakTextInto64ByteChunks(input_message);

//! 由64Byte大小的数据块,构造出64个4Byte大小的字。然后进行循环迭代。
std::vector<uint32_t> message_digest(initial_message_digest_); // 初始化信息摘要
for (const auto &chunk : chunks)
{
transform(structureWords(chunk), message_digest);
}

//! 获取最终结果
return produceFinalHashValue(message_digest);
}

std::string Sha256::getHexMessageDigest(const std::string &message) const
{
auto digest = encrypt(std::vector<uint8_t>(message.begin(), message.end()));

std::ostringstream o_s;
o_s << std::hex << std::setiosflags(std::ios::uppercase);
for (auto c : digest) {
o_s << std::setw(2) << std::setfill('0') << static_cast<unsigned short>(c);
}

return o_s.str();
}

void Sha256::preprocessing(std::vector<uint8_t> &message) const
{
const auto original_bit_size = message.size() * 8;

//! 附加填充比特
auto remainder = message.size() % 64;
auto origialSize = message.size();
if (remainder < 56)
{
message.resize(message.size() + 56 - remainder, 0x00);
message[origialSize] = 0x80; // ox80即10000000
}
else if (remainder == 56)
{
message.resize(message.size() + 64, 0x00);
message[origialSize] = 0x80;
}
else
{
message.resize(message.size() + 120 - remainder, 0x00);
message[origialSize] = 0x80;
}

//! 附加原始文本的长度值
for (int i = 1; i <= 8; ++i)
{
message.emplace_back(static_cast<uint8_t>(original_bit_size >> (64 - 8 * i)));
}
}

std::vector<std::vector<uint8_t>> Sha256::breakTextInto64ByteChunks(const std::vector<uint8_t> &message) const
{
if (0 != message.size() % 64)
{
std::ostringstream oss;
oss << "invalid message size: " << message.size();
throw std::invalid_argument(oss.str());
}

std::vector<std::vector<uint8_t>> chunks;
auto quotient = message.size() / 64;
for (size_t i = 0; i < quotient; ++i)
{
chunks.emplace_back(message.begin() + i * 64, message.begin() + (i + 1) * 64);
}
return chunks;
}

std::vector<uint32_t> Sha256::structureWords(const std::vector<uint8_t> &chunk) const
{
if (64 != chunk.size())
{
std::ostringstream oss;
oss << "invalid chunk size: " << chunk.size();
throw std::invalid_argument(oss.str());
}

std::vector<uint32_t> words(64);
for (size_t i = 0; i < 16; ++i)
{
words[i] = (static_cast<uint32_t>(chunk[i * 4]) << 24) | (static_cast<uint32_t>(chunk[i * 4 + 1]) << 16) |
(static_cast<uint32_t>(chunk[i * 4 + 2]) << 8) | static_cast<uint32_t>(chunk[i * 4 + 3]);
}
for (size_t i = 16; i < 64; ++i)
{
words[i] = smallSigma1(words[i - 2]) + words[i - 7] + smallSigma0(words[i - 15]) + words[i - 16];
}
return words;
}

void Sha256::transform(const std::vector<uint32_t> &words, std::vector<uint32_t> &message_digest) const
{
if (8 != message_digest.size() || 64 != words.size())
{
std::ostringstream oss;
oss << "invalid message_digest size: " << message_digest.size() <<
"Or invalid words size: " << words.size();
throw std::invalid_argument(oss.str());
}

auto d = message_digest;
for (int i = 0; i < 64; ++i)
{
uint32_t temp1 = d[7] + bigSigma1(d[4]) + ch(d[4], d[5], d[6]) + add_constant_[i] + words[i];
uint32_t temp2 = bigSigma0(d[0]) + maj(d[0], d[1], d[2]);

d[7] = d[6];
d[6] = d[5];
d[5] = d[4];
d[4] = d[3] + temp1;
d[3] = d[2];
d[2] = d[1];
d[1] = d[0];
d[0] = temp1 + temp2;
}

for (int i = 0; i < 8; ++i)
{
message_digest[i] += d[i];
}
}

std::vector<uint8_t> Sha256::produceFinalHashValue(const std::vector<uint32_t> &input) const
{
std::vector<uint8_t> output;
for (auto it = input.begin(); it != input.end(); ++it)
{
for (int i = 0; i < 4; i++)
{
output.emplace_back(static_cast<uint8_t>((*it) >> (24 - 8 * i)));
}
}

return output;
}

} // namespace Ly

可视化学习链接

https://sha256algorithm.com/