sha256

sha256

参考文章

https://zhuanlan.zhihu.com/p/94619052

https://www.52pojie.cn/forum.php

实现流程

符号替换-使用函数

  • [公式]: 按位异或
  • [公式]: 按位与
  • [公式]: 按位或
  • [公式]: 补位
  • [公式]: 相加以后对[公式]求余
  • [公式]: 右移n位
  • [公式]: 循环右移n位

使用到的函数

消息预处理

消息补位

先对消息摘要实现补码,假设消息M的二进制编码长度为l位。那么我们现在消息末尾补上一位1,然后再补上k个0,其中k为下列方程的最小非负整数

计算K的公式

特殊情况:当l长度为448时要填充加上512bits,449的话就填充511bits

447的话直接填充一个1即可

最后填充64位,这64位是l的二进制编码

补完位的消息长度为512的倍数

将补位完的消息进行分组

将补码处理后的消息以512位为单位分块为: [公式]

将每个消息块分为16×32bits, 前32位表示为: [公式], 后面32位为: [公式], 以此类推, 最后32位的消息块可表示为: [公式] ,而在C语言中,32位正好对应unsigned int

消息块扩充

这里我们得到的是16个32bits消息块,要把其扩展为64个32bits的消息块

扩展代码

1
2
3
4
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

Hash主要加密流程

常量初始化

初始哈希值取自自然数前8个素数(2,3,5,7,11,13,17,19)的平方根的小数部分的前32位的十六进制表示,比如:

[公式]小数部分约为0.414213562373095048

根号2的十六进制表示

于是,取出前32位对应0x6a09e667

依次得到初始化的H值

初始化H值

sha256的64个常数

取自自然数中前面64个素数的立方根的小数部分的前32位的十六进制表示

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

进行64次循环加密,流程图如下:

主要加密

这里介绍一下ABCDEFGH的值以及K数组,W是我们扩展后的消息块

先把第一个W块和K相加后mod2^32,但是在C语言中,会进行截断,超过32位直接不管超过的那一位

接着先把H7和前一个得到的结果相加后mod2^32

再把H4、H5、H6取出进行Ch加密,再和前一个结果相加后mod2^32

将H4取出进行什么1操作,再和前一步的结果相加后mod2^32

接下来分为两步,第一步把前一个的结果和H3相加后mod232并存储在H4,第二步,先把H0、H1、H2进行Ma操作,再和前一步的结果相加再mod232

然后把H0取出进行什么0操作,再和前面第二步的结果相加后mod32后存储在H0

最后进行移位存储操作,就是除了H0和H4,剩下的H值存入前一个的H值

这个步骤循环64步就是加密后的H值,这个H值会被用于下一个512bits消息块的初始H值,最后的H数组加上原来的中间Hash值就是该消息区块的Hash值

C语言实现

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
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<Windows.h>
#define SHA256_ROTL(a,b) (((a>>(32-b))&(0x7fffffff>>(31-b)))|(a<<b))
#define SHA256_SR(a,b) ((a>>b)&(0x7fffffff>>(b-1)))
#define SHA256_CH(x,y,z) ((x&y)^((~x)&z))
#define SHA256_Maj(x,y,z) ((x&y)^(x&z)^(y&z))
#define SHA256_E0(x) (SHA256_ROTL(x,30)^SHA256_ROTL(x,19)^SHA256_ROTL(x,10))
#define SHA256_E1(x) (SHA256_ROTL(x,26)^SHA256_ROTL(x,21)^SHA256_ROTL(x,7))
#define SHA256_O0(x) (SHA256_ROTL(x,25)^SHA256_ROTL(x,14)^SHA256_SR(x,3))
#define SHA256_O1(x) (SHA256_ROTL(x,15)^SHA256_ROTL(x,13)^SHA256_SR(x,10))

void SHA256_ENCODE(char* input, int len)
{
int i;
char* pos,*posend;
long A, B, C, D, E, F, G, I;
long W[64] = { 0 };
//初始化常量数据
long long H[8] = {
0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,
0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19
};
unsigned int K[64] = {
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,
};
//消息补位,一个字符8位,而448位为56个字符,128个字符是1024位,512的两倍
long long len_final = len + ((len % 64 >= 56) ? (128 - len % 64) : (64 - len % 64));
//printf("%d", len_final);
//开辟空间
if (!(pos = (char*)malloc(len_final)))
{
printf("开辟空间失败!\n");
exit(0);
}
//将字符串赋值到堆中,注意因为后面需要转为long类型运算,所以赋值时要注意大小端序的问题
for ( i = 0; i < len; ++i)
{
pos[i + 3 - 2 * (i % 4)] = input[i];
}
//字符串后面的1和填充0
pos[i + 3 - 2 * (i % 4)] = 0x80;
i++;
for (; i < len_final; ++i)
{
pos[i + 3 - 2 * (i % 4)] = 0;
}
//填充字符串的真实长度,并扩展为64位,64位也就是8个字符
*((long*)(pos + len_final - 4)) = len << 3;
*((long*)(pos + len_final - 8)) = len >> 29;
//每512位进行加密
for (posend = pos + len_final; pos < posend; pos += 64)
{
//先分组为16*32bit
for (int j = 0; j < 16; ++j)
{
W[j] = ((long*)pos)[j];
}
//扩展为64*32bit
for (int j = 16; j < 64; ++j)
{
W[j] = (SHA256_O1(W[j - 2]) + W[j - 7] + SHA256_O0(W[j - 15]) + W[j - 16]);
}
//下面开始进行64轮加密
//因为中间哈希值最后时为原来的+加密的哈希值,所以先储存
A = H[0], B = H[1], C = H[2], D = H[3], E = H[4], F = H[5], G = H[6], I = H[7];
for (i = 0; i < 64; ++i)
{
//T1存储加密的前一部分,T2存储加密的后一部分
long T1 = I + SHA256_CH(E, F, G) + K[i] + W[i]+SHA256_E1(E);
long T2 = SHA256_Maj(A, B, C) + SHA256_E0(A);
//只能从后往前赋值,否则会覆盖
I = G;
G = F;
F = E;
E = D + T1;
D = C;
C = B;
B = A;
A = T1 + T2;
}
H[0] += A; H[1] += B;
H[2] += C; H[3] += D;
H[4] += E; H[5] += F;
H[6] += G; H[7] += I;
for (i = 0; i < 8; ++i)
{
printf("%X", H[i]);
}
}
return;
}
int main()
{
char input[] = "ze";
SHA256_ENCODE(input, strlen(input));
return 0;
}