sha256 学习笔记

step1 常量初始化

1
2
3
4
5
6
7
8
h0 := 0x6a09e667
h1 := 0xbb67ae85
h2 := 0x3c6ef372
h3 := 0xa54ff53a
h4 := 0x510e527f
h5 := 0x9b05688c
h6 := 0x1f83d9ab
h7 := 0x5be0cd19

这八个初始向量怎么来的?
答曰: 前八个质数取根号后的值 取小数部分, 然后乘以2的32次方, 对结果取整 就是初始向量.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import math

# 第一个8个素数
primes = [2, 3, 5, 7, 11, 13, 17, 19]

H = []

for p in primes:
sqrt_p = math.sqrt(p)
sqrt_p = p ** (1/2)
frac_part = sqrt_p - int(sqrt_p) # 提取小数部分
value = int(frac_part * (2**32)) # 小数部分 * 2^32,再取整
H.append(value)

# 打印结果
for h in H:
print(hex(h))

step2 常量64位 K表

如果生成的?
答曰: 前64个质数,取立方根, 跟8位常量的生成算法一样, 只是取的是立方根, 下面给出代码

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
import math

# 先生成前64个素数
def get_first_n_primes(n):
primes = []
candidate = 2
while len(primes) < n:
is_prime = True
for p in primes:
if p * p > candidate:
break
if candidate % p == 0:
is_prime = False
break
if is_prime:
primes.append(candidate)
candidate += 1
return primes

# 计算K表
def generate_sha256_k():
primes = get_first_n_primes(64)
K = []
for p in primes:
cbrt = p ** (1/3)
frac_part = cbrt - int(cbrt)
value = int(frac_part * (2**32))
K.append(value)
return K

# 打印结果
K = generate_sha256_k()
for i, k in enumerate(K):
print(f"K[{i}] = 0x{k:08x}")

综上,这些常量也只是根据质数生成的, 换一批质数, 获取随机常量就可以达到魔改的目的

step 3 填充

假设有数据 ‘abc’, 用16进制表示 是

1
2
61626300 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

0x61 是 ‘a’的 ascii 码
0x62 是 ‘b’的 ascii 码
0x63 是 ‘c’的 ascii 码

将数据尾部添加一个 bit’1’, 然后再添加若干个 bit’0’, 凑够 448 位
448 + 64 = 512, 为什么要凑 448 因为还有 64位置是用来记录数据的长度的。 448位记录是的数据 64位记录的是长度

这样数据在内存中就变成了如下分布

1
2
61626380 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000018

0x80 是 10000000的hex表示.
0x18 对应十进制是 24, 正好就是 abc 所占的bit

计算, 生成 w0-w63

w0-w15 就是原来的数据.

w16-w63 根据如下算法生成

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
def _sigma0(num: int):
"""As defined in the specification."""
num = (_rotate_right(num, 7) ^
_rotate_right(num, 18) ^
(num >> 3))
return num

def _sigma1(num: int):
"""As defined in the specification."""
num = (_rotate_right(num, 17) ^
_rotate_right(num, 19) ^
(num >> 10))
return num

def _capsigma0(num: int):
"""As defined in the specification."""
num = (_rotate_right(num, 2) ^
_rotate_right(num, 13) ^
_rotate_right(num, 22))
return num

def _capsigma1(num: int):
"""As defined in the specification."""
num = (_rotate_right(num, 6) ^
_rotate_right(num, 11) ^
_rotate_right(num, 25))
return num

def _ch(x: int, y: int, z: int):
"""As defined in the specification."""
return (x & y) ^ (~x & z)

def _maj(x: int, y: int, z: int):
"""As defined in the specification."""
return (x & y) ^ (x & z) ^ (y & z)

def _rotate_right(num: int, shift: int, size: int = 32):
"""Rotate an integer right."""
return (num >> shift) | (num << size - shift)


W = []
for t in range(0, 64):
if t <= 15:
# adds the t'th 32 bit word of the block,
# starting from leftmost word
# 4 bytes at a time
W.append(bytes(message_block[t*4:(t*4)+4]))
else:
term1 = _sigma1(int.from_bytes(W[t-2], 'big'))
term2 = int.from_bytes(W[t-7], 'big')
term3 = _sigma0(int.from_bytes(W[t-15], 'big'))
term4 = int.from_bytes(W[t-16], 'big')

# append a 4-byte byte object
schedule = ((term1 + term2 + term3 + term4) % 2**32).to_bytes(4, 'big')
W.append(schedule)

assert len(W) == 64

接下给出sha256 完整代码

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
"""This Python module is an implementation of the SHA-256 algorithm.
From https://github.com/keanemind/Python-SHA-256"""

K = [
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
]

def generate_hash(message: bytearray) -> bytearray:
"""Return a SHA-256 hash from the message passed.
The argument should be a bytes, bytearray, or
string object."""

if isinstance(message, str):
message = bytearray(message, 'ascii')
elif isinstance(message, bytes):
message = bytearray(message)
elif not isinstance(message, bytearray):
raise TypeError

# Padding
length = len(message) * 8 # len(message) is number of BYTES!!!
message.append(0x80)
while (len(message) * 8 + 64) % 512 != 0:
message.append(0x00)

message += length.to_bytes(8, 'big') # pad to 8 bytes or 64 bits

assert (len(message) * 8) % 512 == 0, "Padding did not complete properly!"

# Parsing
blocks = [] # contains 512-bit chunks of message
for i in range(0, len(message), 64): # 64 bytes is 512 bits
blocks.append(message[i:i+64])

# Setting Initial Hash Value
h0 = 0x6a09e667
h1 = 0xbb67ae85
h2 = 0x3c6ef372
h3 = 0xa54ff53a
h5 = 0x9b05688c
h4 = 0x510e527f
h6 = 0x1f83d9ab
h7 = 0x5be0cd19

# SHA-256 Hash Computation
for message_block in blocks:
# Prepare message schedule
message_schedule = []
for t in range(0, 64):
if t <= 15:
# adds the t'th 32 bit word of the block,
# starting from leftmost word
# 4 bytes at a time
message_schedule.append(bytes(message_block[t*4:(t*4)+4]))
else:
term1 = _sigma1(int.from_bytes(message_schedule[t-2], 'big'))
term2 = int.from_bytes(message_schedule[t-7], 'big')
term3 = _sigma0(int.from_bytes(message_schedule[t-15], 'big'))
term4 = int.from_bytes(message_schedule[t-16], 'big')

# append a 4-byte byte object
schedule = ((term1 + term2 + term3 + term4) % 2**32).to_bytes(4, 'big')
message_schedule.append(schedule)

assert len(message_schedule) == 64

# Initialize working variables
a = h0
b = h1
c = h2
d = h3
e = h4
f = h5
g = h6
h = h7

# Iterate for t=0 to 63
for t in range(64):
t1 = ((h + _capsigma1(e) + _ch(e, f, g) + K[t] +
int.from_bytes(message_schedule[t], 'big')) % 2**32)

t2 = (_capsigma0(a) + _maj(a, b, c)) % 2**32

h = g
g = f
f = e
e = (d + t1) % 2**32
d = c
c = b
b = a
a = (t1 + t2) % 2**32

# Compute intermediate hash value
h0 = (h0 + a) % 2**32
h1 = (h1 + b) % 2**32
h2 = (h2 + c) % 2**32
h3 = (h3 + d) % 2**32
h4 = (h4 + e) % 2**32
h5 = (h5 + f) % 2**32
h6 = (h6 + g) % 2**32
h7 = (h7 + h) % 2**32

return ((h0).to_bytes(4, 'big') + (h1).to_bytes(4, 'big') +
(h2).to_bytes(4, 'big') + (h3).to_bytes(4, 'big') +
(h4).to_bytes(4, 'big') + (h5).to_bytes(4, 'big') +
(h6).to_bytes(4, 'big') + (h7).to_bytes(4, 'big'))

def _sigma0(num: int):
"""As defined in the specification."""
num = (_rotate_right(num, 7) ^
_rotate_right(num, 18) ^
(num >> 3))
return num

def _sigma1(num: int):
"""As defined in the specification."""
num = (_rotate_right(num, 17) ^
_rotate_right(num, 19) ^
(num >> 10))
return num

def _capsigma0(num: int):
"""As defined in the specification."""
num = (_rotate_right(num, 2) ^
_rotate_right(num, 13) ^
_rotate_right(num, 22))
return num

def _capsigma1(num: int):
"""As defined in the specification."""
num = (_rotate_right(num, 6) ^
_rotate_right(num, 11) ^
_rotate_right(num, 25))
return num

def _ch(x: int, y: int, z: int):
"""As defined in the specification."""
return (x & y) ^ (~x & z)

def _maj(x: int, y: int, z: int):
"""As defined in the specification."""
return (x & y) ^ (x & z) ^ (y & z)

def _rotate_right(num: int, shift: int, size: int = 32):
"""Rotate an integer right."""
return (num >> shift) | (num << size - shift)

if __name__ == "__main__":
print(generate_hash("Hello").hex())