0%

SHA-512算法3-完整流程

SHA-512的完整实现我总结下来,总共分三步,一是填充补充位和长度;二是初始化8个变量;三是循环计算这8个变量最终输出.

1. 填充位和长度信息

整个需要SHA-512的消息块必须是1024位的整数倍,需要补足还要有真实的长度信息.所以消息长度模896,有余数补充一个1和N个0组成;最后加上128位里表示消息的真实长度.这个处理也可以在处理最后一块1024的时候来处理.

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
int SHA512_Final(unsigned char *md, SHA512_CTX *c) {
// 省略...

// 在需要填充的部分先填充一个0x80
// #define SHA512_PADDING_PATTERN 0x80
// #define HASH_PADDING_PATTERN SHA512_PADDING_PATTERN
c->last.buf[c->last.used] = HASH_PADDING_PATTERN;
c->last.used++;

// 余下的部分全部填0
// #define SHA512_BLOCK_SIZE 128 /* 1024 bits = 128 bytes */
// #define HASH_BLOCK_SIZE SHA512_BLOCK_SIZE

// #define SHA512_LEN_SIZE 16 /* 128 bits = 16 bytes */
// #define HASH_LEN_SIZE SHA512_LEN_SIZE
memset(&c->last.buf[c->last.used], 0,
HASH_BLOCK_SIZE - HASH_LEN_SIZE - c->last.used);

// 在把真实长度补上
// #define SHA512_LEN_OFFSET (SHA512_BLOCK_SIZE - SHA512_LEN_SIZE)
// SHA512_SaveTotal(&c->last.buf[HASH_LEN_OFFSET], &c->total);
temp = (uint64_t *)&(c->last.buf[HASH_LEN_OFFSET]);
temp[0] = htobe64(c->total.high);
temp[1] = htobe64(c->total.low);

SHA512_ProcessBlock(c, &c->last.buf);

// 省略...
}

2. 初始化8个变量

自然数前8个素数开平方根,取小数部分的前64位.
计算开平方根,平方根的计算这篇文章讲的非常好.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int SHA512_Init(SHA512_CTX *c) {
if (NULL == c) {
return ERR_INV_PARAM;
}
memset(c, 0, sizeof(SHA512_CTX));

/* Initial Value for SHA512 */
c->hash.a = 0x6a09e667f3bcc908;
c->hash.b = 0xbb67ae8584caa73b;
c->hash.c = 0x3c6ef372fe94f82b;
c->hash.d = 0xa54ff53a5f1d36f1;
c->hash.e = 0x510e527fade682d1;
c->hash.f = 0x9b05688c2b3e6c1f;
c->hash.g = 0x1f83d9abfb41bd6b;
c->hash.h = 0x5be0cd19137e2179;

c->total.low = 0;
c->total.high = 0;
c->last.used = 0;
return ERR_OK;
}

3. 单组1024位摘要信息计算

单轮函数的计算公式参考SHA-512算法2-轮函数计算

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
int SHA512_ProcessBlock(SHA512_CTX *ctx, const void *block) {
uint32_t t;
uint64_t W[HASH_ROUND_NUM];
uint64_t T1, T2;
uint64_t a, b, c, d, e, f, g, h;

if ((NULL == ctx) || (NULL == block)) {
return ERR_INV_PARAM;
}

/* prepare schedule word */
// 这个函数就是计算需要参与计算摘要的$W_t$
SHA512_PrepareScheduleWord(block, W);

// 赋值8个临时变量
// 第一个1024块 初始值来源参见上一节
// 其他1024块来源于上一个块的计算值
// SHA512_Init
a = ctx->hash.a;
b = ctx->hash.b;
c = ctx->hash.c;
d = ctx->hash.d;
e = ctx->hash.e;
f = ctx->hash.f;
g = ctx->hash.g;
h = ctx->hash.h;

// 参看上图所示一共HASH_ROUND_NUM(80)轮
// 最终计算得到H(a,b,c,d,e,f,g,h)
for (t=0; t<HASH_ROUND_NUM; t++) {
// W和K512都参与影响e和a
T1 = h + SIGMA1(e) + Ch(e, f, g) + K512[t] + W[t];
T2 = SIGMA0(a) + Maj(a, b, c);
h = g;
g = f;
f = e;
e = d + T1;
d = c;
c = b;
b = a;
a = T1 + T2;
}

// 为下一轮的变量做准备
ctx->hash.a += a;
ctx->hash.b += b;
ctx->hash.c += c;
ctx->hash.d += d;
ctx->hash.e += e;
ctx->hash.f += f;
ctx->hash.g += g;
ctx->hash.h += h;

return ERR_OK;
}

4. 总的计算过程

1
2
3
4
5
6
7
8
9
10
11
12
13
int SHA512_Update(SHA512_CTX *c, const void *data, size_t len) {
// 省略
/* process data blocks */
while (len >= HASH_BLOCK_SIZE) {
// 结果记录在c中,data为当前要处理的1024块。
SHA512_ProcessBlock(c, data);
SHA512_UpdateTotal(&c->total, HASH_BLOCK_SIZE);

data = (uint8_t *)data + HASH_BLOCK_SIZE;
len -= HASH_BLOCK_SIZE;
}
// 省略
}

代码详情:https://gitee.com/korra/lib3rd.git
git checkout sha512分支。或者 点击下载
结果比对工具:https://1024tools.com/hash