以太坊在许多地方使用_Keccak-256_加密哈希函数。Keccak-256被设计为于2007年举行的SHA-3密码哈希函数竞赛的候选者。Keccak是获胜的算法,在2015年被标准化为 FIPS(联邦信息处理标准)。

不过NIST接受原始的Keccak256设计后,更改了Padding的格式, 以太坊坚持使用了原始的方案,因为这一更改存在争议,导致了正式的SHA3实现和原始的Keccak不兼容。

用途
为了隐藏起某些信息,且保证这些信息不被篡改,需要用到哈希算法。keccak256算法则可以将任意长度的输入压缩成64位16进制的数,且哈希碰撞的概率近乎为0.

输出样例如下:

keccak256(“aaaab”);
//6e91ec6b618bb462a4a6ee5aa2cb0e9cf30f7a052bb467b0ba58b8748c00d2e5
keccak256(“aaaac”);
//b1f078126895a1424524de5321b339ab00408010b7cf0e6ed451514981e58aa9
1
2
3
4
实现原理
符号定义
Keccak算法使用以下符号与函数:

符号

r:比特率(比特 rate),其值为每个输入块的长度
c:容量(capacity),其长度为输出长度的两倍

b:向量的长度,b=r+c,而b的值依赖于指数I,即b=25×2I

 

整体架构

吸入与挤出
吸入主要是对输入进行补0操作,挤出是将处理后的输出转为固定长度的输出,挤出的主要流程如下:
Theta Step (θ)
C[x] = A[x,0] xor A[x,1] xor A[x,2] xor A[x,3] xor A[x,4], for x in 0…4
D[x] = C[x-1] xor rot(C[x+1],1), for x in 0…4
A[x,y] = A[x,y] xor D[x], for (x,y) in (0…4,0…4)
1
2
3
Rho (ρ) and Pi (π) Steps
B[y,2*x+3*y] = rot(A[x,y], r[x,y]), for (x,y) in (0…4,0…4)
1
Chi (χ) Step
A[x,y] = B[x,y] xor ((not B[x+1,y]) and B[x+2,y]), for (x,y) in (0…4,0…4)
1
Iota (ι) Step
A[0,0] = A[0,0] xor RC
1
代码
#include “keccak.h”
#include <cstring>
#include <cstdio>

//rot(num, offset)表示将w比特的num向z轴正方向循环移动offset位。具体实现中,可以当成循环左移offset位
uint64_t Keccak::rot(uint64_t num, int offset) {
if (offset == 0) {
return num;
}
return (num << offset) | (num >> (64 – offset));
}

void Keccak::theta(uint64_t input[MAT][MAT], uint64_t output[MAT][MAT]) {
// 整行异或生成一个值,二维变一维
for (int i = 0; i < MAT; ++i) {
theta_C[i] = 0x0;
for (int j = 0; j < MAT; ++j) {
theta_C[i] ^= input[i][j];
}
}

// 之前得到的一维数组再次异或变换
for (int i = 0; i < MAT; ++i) {
theta_D[i] = theta_C[(i – 1 + MAT) % MAT] ^ rot(theta_C[(i + 1) % MAT], 1);
}

// 输入数组与变换后的一维数组异或
for (int i = 0; i < MAT; ++i) {
for (int j = 0; j < MAT; ++j) {
output[i][j] = input[i][j] ^ theta_D[i];
}
}
}

//将数组数组向z方向位移,再赋给输出
void Keccak::rho_pi(uint64_t input[MAT][MAT], uint64_t output[MAT][MAT]) {
for (int i = 0; i < MAT; ++i) {
for (int j = 0; j < MAT; ++j) {
output[j][(2 * i + 3 * j) % MAT] = rot(input[i][j], R_CONS[i][j]);
}
}
// printf(“%lld\n”, output[0][0]);
}

//取反异或
void Keccak::chi(uint64_t input[MAT][MAT], uint64_t output[MAT][MAT]) {
for (int i = 0; i < MAT; ++i) {
for (int j = 0; j < MAT; ++j) {
output[i][j] = input[i][j] ^ ((~input[(i + 1) % MAT][j]) & input[(i + 2) % MAT][j]);
}
}
}

//首元素初始化
void Keccak::iota(uint64_t input[MAT][MAT], int round) {
input[0][0] ^= RC[round];
}

void Keccak::keccak_f(int round) {
// 主要在做输入的异或变换
theta(after_f, after_theta);
// 输入转输出
rho_pi(after_theta, after_rho_pi);
// 取反异或
chi(after_rho_pi, after_f);
// 首元素初始化
iota(after_f, round);
}

unsigned char* Keccak::encrypt(unsigned char*seq, int seq_len) {
uint8_t *pad_seq;
int block;
int pad_zero = 0;
if ((seq_len + MIN_PAD) % r != 0) {
pad_zero = r – (seq_len + MIN_PAD) % r;
}
block = (seq_len + MIN_PAD + pad_zero) / r;
// 输入字符串进行pad,补0
pad_seq = new uint8_t[seq_len + MIN_PAD + pad_zero];
memcpy(pad_seq, seq, seq_len);
// 输入字符串添加结束符P
pad_seq[seq_len] = P;
if (pad_zero > 0) {
memset(pad_seq + seq_len + 1, 0, pad_zero);
}

pad_seq[seq_len + MIN_PAD + pad_zero – 1] |= 0x80;

for (int i = 0; i < MAT; ++i) {
for (int j = 0; j < MAT; ++j) {
after_f[i][j] = 0;
}
}

for (int i = 0; i < block; ++i) {
int pos = 0;
for (int j = i * r; j < (i + 1) * r; j += 8) {
uint64_t t = 0;
for (int k = j + 7; k >= j; –k) {
t = (t << 8) | pad_seq[k];
}
// pad字符串转为二维数组
after_f[pos % MAT][pos / MAT] ^= t;
pos++;
}
for (int j = 0; j < nr; ++j) {
keccak_f(j);
}
}

for (int i = 0; i < output_len; ) {
for (int j = 0; j < 8; ++j) {
result[i] = after_f[(i / 8) % MAT][(i / 8) / MAT] & 0xff;
after_f[(i / 8) % MAT][(i / 8) / MAT] >>= 8;
i++;
if (i >= output_len) {
break;
}
}
}
return result;
}

//int main() {
//
// unsigned char chs[7] = “ssss1s”;
// uint8_t * temp ;
// temp = Keccak(“256”).encrypt(chs, 6);
//
// for (int i=0;i<256;i++){
// printf(“%d”,temp[i]);
// }
//
// return 0;
//}

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
输出
通过海绵结构中的挤出阶段,可以获得任意长度的输出。在Keccak-224/256/384/512中,我们只需要获得y0中的前224/256/384/512个bit作为输出即可。

参考
现代密码学:Hash函数Keccak_ayang1986的博客-CSDN博客_keccak
————————————————
版权声明:本文为CSDN博主「正如此时」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_50665031/article/details/124214062