#声明:本文创作内容含代码均为个人创作所得,允许学习、传阅,不得用于商业用途#
#本文包含SHA-256从算法到硬件实现的全部#
#实践会让你更加透彻理解算法原理,才能让知识灌入大脑#
在开始前请先容许我MMP 联邦信息处理标准,因为你们给出的PDF有误,导致我denbug到了凌晨两点半。
SHA-256整个算法架构和SM3一样,第一步拼接,第二步迭代,区别是迭代过程中的对数据逻辑运算不一样,这里算法我就简单说明,大家可以和专栏里面SM3对比起来看。
一:背景
SHA(Secury Hash Standrad)是一种信息摘要码(MDC),主要应用信息传输,加解密,存储等信息领域,简单理解就是任何一组消息,都可以压缩成一个固定位宽的长度数据,只要消息中的任何1 比特发生改变,该消息的hash值就会发生变化。
比如,我们从某网页下载一个软件包,一般该网页会给出这个软件包的摘要值,当我们下载后,可以计算下载后软件包的摘要值,并与官网比较,看是否是一样,如果一样,则可以认为下载过程中,没有出现信息错误,如果摘要值不一样,则信息在通过网络传输出现了误码情况
SHA有多种标准,如SHA-1,SHA-256,SHA-384,SHA-512等,这些不同标准的区别在于生成的摘要值的长度不一样,SHA-256生成摘要长度为256比特,这些不同标准的算法实现不一样,但算法架构一样,那为啥出现这么多种标准呢?因为应用场景对安全性的要求不一样;我们可以简单认为一段消息的摘要值是确定的,消息改变了,摘要值也改变,但如果消息很长,其中某些比特出现错位,其消息摘要值不变,这种概率是存在的,但很小;不同标准对应的概率不一样。
二 算法说明
我们以SHA-256来说明
第一步填充:
目的是为了确保消息的bit长度是512的倍数
先把bit 1 附加在消息尾部,然后再附加上k个 bit 0,使得 L+ K+ 1 = 448 mode 512,这里L是消息二进制的bit长度;然后再把L转化为一个64 bit 二进制数,如果二进制数位宽不够,则高位补0,然后把这 64bit 附加在上一步操作后的消息的尾部
例如下图:
说明:这里我们消息为 ‘abc’,一个三个字母,这里每个字母的ASIIC的表示是8个bit,那消息的bit 长度是24,十进制24用二进制表示就是11000,那填充后的消息长度是:8+8+8+423+64 = 512
第二步模块分解:
消息按照每512bit来作为一组,我们把每个512bit 分成16个字(32bit),根据这16个字,我们迭代出64个字W0,W1, ...,W63, 即{W0, W1,W2...,W15} = 512bit block消息
其中W0~ W15就是一组消息里面的16个字,然后剩下的W16~W63根据下面公式迭代:
i=16.17...63
每一组512bit消息都要这么多迭代,假设有M个512bit消息,那就会有M组Wi,其中i=0~~63;为啥要每组要迭代出64个Wi呢?因为每组512bit消息需要经过64轮迭代,每一轮迭代需要一个Wi,所以上面每512bit消息生成了64个Wi;
说明:公式里的sigma0,sigma1是一个函数,具体形式后面给出
第三步迭代计算
填充后的消息块我们用,,...,来表示,每个M是512bit,前个 i 个M数据块对应的hash值为,初始的256bit hash值用如下8个字来表示,上角标对应数据块标记,下角标对应hash的字
每一轮的迭代过程如下:
For k = 16 to 63
注释:先迭代出所需的64个Wi
注释:将上一轮的hash赋给中间变量
For i = 0 to 63 然后再计算本轮的hash
公式 1-1
注释:本轮的hash值等于上一轮的hash对应加上a, b, c, d, e, f, g, h
经过N次迭代后得到最终的hash值 H----256bit
上式 1-1 里面的K是64个32bit的常数,他们是前64个质素的立方根的小数部分的前32bit,
举个例子,第一个质数为2,立方根为1.25992104...., 我们将其小数部分用二进制数表示,为
428a2f98。因为这64个字是常数,所以直接拿来用就可以,如下图:
且上述初始化hash值为前8个质数的平方根的小数部分的32bit,也是这么计算得来的
上式1-1里的那些函数的具体形式如下:
其中RotR(X, n)是X循环右移n位,ShR(X,n)是右移n位,切记注意注意注意
^ 为 bit 与, 为bit异或,- 为 bit 取反
注意注意注意:前面公式中的 + 是基于模2^32 的加法运算,这有区别于GF(2, 32),切记,切记,切记,而 与、异或、取反是基于GF(2, 32)
三 python 代码如下
说明:python代码中消息可以是字符,也可以是16进制数,参数mode来配置,
python中给出了三个消息的例子,读者可以在代码中加入打印,来调试代码
通过抄写来理解算法原理
class Sha256:
""" all function is based on sting, thr string is 32bit hex value """
K_PARAMETER = ['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']
H_PARAMETER = ['6a09e667', 'bb67ae85', '3c6ef372', 'a54ff53a', '510e527f', '9b05688c','1f83d9ab', '5be0cd19']
def shift_right(self, x, k):
# suppose data is 32 bit -- word
k = k % 32
return (x >> k) & 0xFFFFFFFF
def rotate_right(self, x, k):
# suppose data is 32 bit --word
k = k % 32
return ((x >> k) & 0xFFFFFFFF) | ((x & 0xFFFFFFFF) << (32-k)) & 0xFFFFFFFF
def ch_function(self, x, y, z):
tmp = (x & y & 0xFFFFFFFF) ^ ( (0xFFFFFFFF - (x & 0xFFFFFFFF)) & z) & 0xFFFFFFFF
return tmp
def mag_function(self, x, y, z):
tmp = (x & y & 0xFFFFFFFF) ^ (x & z & 0xFFFFFFFF) ^ (y & z & 0xFFFFFFFF) & 0xFFFFFFFF
return tmp
def sigma_0_b(self, x):
tmp = self.rotate_right(x, 2) ^ self.rotate_right(x, 13) ^ self.rotate_right(x, 22) & 0xFFFFFFFF
return tmp
def sigma_1_b(self, x):
tmp = self.rotate_right(x, 6) ^ self.rotate_right(x, 11) ^ self.rotate_right(x, 25) & 0xFFFFFFFF
return tmp
def sigma_0_s(self, x):
tmp = self.rotate_right(x, 7) ^ self.rotate_right(x, 18) ^ self.shift_right(x, 3) & 0xFFFFFFFF
return tmp
def sigma_1_s(self, x):
tmp = self.rotate_right(x, 17) ^ self.rotate_right(x, 19) ^ self.shift_right(x, 10) & 0xFFFFFFFF
return tmp
def str2byte(self, string):
hex_array = []
string_encode = string.encode()
for char in string:
hex_array.append(int(ord(char)))
return hex_array
def num2byte(self, string):
hex_array = []
string_len = len(string)
if (string_len % 2):
string = '0' + string
for i in range(int(string_len / 2)):
hex_array.append(int(string[i * 2:i * 2 + 2], 16))
return hex_array
def padding(self, string, num):
if (num == 0):
data = self.str2byte(string)
bit_len = len(string) * 8 # each num is 8 bit
else:
data = self.num2byte(string)
bit_len = len(data) * 8 # each num is 8 bit
string_0_len = 512 - (bit_len + 1 + 64) % 512
string_0 = '1' + ('0' * string_0_len) # internal bin
string_64_bit = '0' * (64 - (len(bin(bit_len)) - 2)) + bin(bit_len)[2:] # last 64bit
padding_bit = string_0 + string_64_bit
for i in range(int(len(padding_bit) / 8)): # message to 16 word(32bit)
data.append(int(padding_bit[8 * i:8 * (i + 1)], 2))
# translate to word, cause we are based on word to calculate
message = ['']
for i in range(int(len(data)/4)):
message.append('{:02x}'.format(data[4*i]) + '{:02x}'.format(data[4*i+1]) + '{:02x}'.format(data[4*i+2]) + '{:02x}'.format(data[4*i+3]))
return message[1:]
def sha_iteration(self, date, mode):
message = self.padding(date, mode)
len_message = int(len(message)/16) # how much num of 512bit are string
hash_value = [0] * (len_message * 8 + 8)
# hash_value[0:8] = [a, b, c, d, e, f, g, h]
hash_value[0:8] =[int(self.H_PARAMETER[0], 16), int(self.H_PARAMETER[1], 16), int(self.H_PARAMETER[2], 16), int(self.H_PARAMETER[3], 16), int(self.H_PARAMETER[4], 16), int(self.H_PARAMETER[5], 16), int(self.H_PARAMETER[6], 16), int(self.H_PARAMETER[7], 16)]
for i in range(len_message):
[a, b, c, d, e, f, g, h] = hash_value[8*i:8*(i+1)]
message_block = message[16*i:16*(i+1)]
w = [''] * 64
w[0:16] = message_block
for j in range(16, 64):
w[j] = '{:08x}'.format(self.sigma_1_s(int(w[j-2], 16)) + int(w[j-7], 16) + self.sigma_0_s( int( w[j-15], 16)) + int(w[j-16], 16) & 0xFFFFFFFF)
for t in range(64):
T1 = ( h + self.sigma_1_b(e) + self.ch_function(e, f, g) + int(self.K_PARAMETER[t], 16) + int(w[t], 16) ) & 0xFFFFFFFF
T2 = (self.sigma_0_b(a) + self.mag_function(a, b, c)) & 0xFFFFFFFF
h = g
g = f
f = e
e = (d + T1) & 0xFFFFFFFF
d = c
c = b
b = a
a = (T1 + T2) & 0xFFFFFFFF
# print(t, hex(a), hex(b), hex(c), hex(d), hex(e), hex(f), hex(g), hex(h))
hash_value[(i + 1) * 8 + 0] = (a + hash_value[8 * i + 0] ) & 0xFFFFFFFF
hash_value[(i + 1) * 8 + 1] = (b + hash_value[8 * i + 1] ) & 0xFFFFFFFF
hash_value[(i + 1) * 8 + 2] = (c + hash_value[8 * i + 2] ) & 0xFFFFFFFF
hash_value[(i + 1) * 8 + 3] = (d + hash_value[8 * i + 3] ) & 0xFFFFFFFF
hash_value[(i + 1) * 8 + 4] = (e + hash_value[8 * i + 4] ) & 0xFFFFFFFF
hash_value[(i + 1) * 8 + 5] = (f + hash_value[8 * i + 5] ) & 0xFFFFFFFF
hash_value[(i + 1) * 8 + 6] = (g + hash_value[8 * i + 6] ) & 0xFFFFFFFF
hash_value[(i + 1) * 8 + 7] = (h + hash_value[8 * i + 7] ) & 0xFFFFFFFF
return hash_value[len_message * 8:len_message*8 +8]
if __name__ == '__main__':
get_class = Sha256()
# hash_256 = get_class.sha_iteration('abc', 0)
# hash_256 = get_class.sha_iteration('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', 0)
hash_256 = get_class.sha_iteration('a'* 1000000, 0)
for i in range(len(hash_256)):
print(hex(hash_256[i]))
print(hash_256)