常见Hash算法
Frey October 11, 2022 [算法] #Hash什么是hash算法
Hash算法又称散列算法,特点是把任意长度的输出,通过一些列的计算后变成固定长度的输出,这个输出值即为散列值。由于散列值的空间远小于输出空间,因此存在不同输入得到相同输出的情况,这种现象称为碰撞。
hash算法的应用
hash算法应用广泛,主要有七个方面:安全加密、唯一标识、数据校验、散列函数、负载均衡、数据分片、分布式存储。不同的应用利用的hash算法不同的特性,具体的算法实现也需要根据情况设计。比较常见的实现有md5,sha256等。本文通过这两个算法来研究具体的实现。
MD5
Md5算法将输入的不定长数据分为512bit的块,并对每个块循环调用MD5运算。
一个MD5运算由类似的64次循环构成,分成4组16次。F是一个非线性函数;一个函数运算一次。Mi 表示一个 32-bits 的输入数据,Ki 表示一个 32-bits 常数,用来完成每次不同的计算。
md5的python实现
# 定义常量,用于初始化128位变量,注意字节顺序,A=0x01234567,这里低值存放低字节,
# 即01 23 45 67,所以运算时A=0x67452301,其他类似。
# 用字符串的形势,是为了和hex函数的输出统一,hex(10)输出为'0xA',注意结果为字符串。
= 0x67452301
= 0xefcdab89
= 0x98badcfe
= 0x10325476
# 定义每轮中循环左移的位数,用元组表示 4*4*4=64
= * 4 + * 4 + \
* 4 + * 4
# 定义常数K 64
# K[i] = (int(abs(math.sin(i + 1)) * 2 ** 32)) & 0xffffffff
=
# 定义每轮中用到的函数。L为循环左移,
# 左移之后可能会超过32位,所以要和0xffffffff做与运算,确保结果为32位。
=
=
=
=
=
# 小端 0x12,0x34,0x56,0x78 -> 0x78563412
# 将四个8位无符号数转化为一个32位无符号数
=
# 字节翻转 0x12345678 -> 0x78563412 将一个32位无符号数的高位和低位进行对换
=
# 对每一个输入先添加一个'0x80',即'10000000', 即128
=
= * 8
# 补充0
# 最后64为存放消息长度,以小端数存放。
# 例如,消息为'a',则长度是8,则添加'0x0800000000000000'
# print(ascii_list)
# print(len(ascii_list)//64)
# 对每一消息块进行迭代
# print(ascii_list[i*64:(i+1)*64])
# 对每一个消息块进行循环,每个消息块512bits=16*32bits=64*8bits
, , , = , , ,
# 64轮的主循环
= & 0xffffffff
=
= & 0xffffffff
= % 16
= & 0xffffffff
= % 16
= & 0xffffffff
= % 16
, , = , ,
# 第i个chunk,第g个32-bit
= * 64 + * 4
=
= & 0xffffffff
, , , = , , ,
# print(b)
= & 0xffffffff
= & 0xffffffff
= & 0xffffffff
= & 0xffffffff
, , , = , , ,
= | | |
return
=
Shell命令md5sum
|
SHA256
SHA256是安全散列算法2(SHA-2,Secure Hash Algorithm 2)中的一个算法标准,由美国国家标准与技术研究院(NIST)在2001年发布。
SHA-2的第t个加密循环。图中的深蓝色方块是事先定义好的非线性函数。ABCDEFGH一开始分别是八个初始值,Kt是第t个密钥,Wt是本区块产生第t个word。原消息被切成固定长度的区块,对每一个区块,产生n个word(n视算法而定),透过重复运作循环n次对ABCDEFGH这八个工作区段循环加密。最后一次循环所产生的八段字符串合起来即是此区块对应到的散列字符串。若原消息包含数个区块,则最后还要将这些区块产生的散列字符串加以混合才能产生最后的散列字符串。
SHA256的python实现
# 定义常量
# 前8个素数2..19的平方根的小数部分的前32位
= 0x6a09e667
= 0xbb67ae85
= 0x3c6ef372
= 0xa54ff53a
= 0x510e527f
= 0x9b05688c
= 0x1f83d9ab
= 0x5be0cd19
# 定义常数K 64
# 前64个素数2..311的立方根的小数部分的前32位
=
# R为循环右移,
# 右移之后可能会超过32位,所以要和0xffffffff做与运算,确保结果为32位。
=
# 大端 0x12,0x34,0x56,0x78 -> 0x12345678
=
# 对每一个输入先添加一个'0x80',即'10000000', 即128
=
= * 8
# 补充0
# 最后64为存放消息长度,以大端数存放。
# 例如,消息为'a',则长度为'0x0000000000000008'
# print(ascii_list)
# print(len(ascii_list)//64)
# 64*8=512bits
# print(ascii_list[i*64:(i+1)*64])
# 每个512bits的块进行循环
=
# 将512bits扩展到64*32bits=2048bits存入32位无符号数数组
= * 64 + * 4
= ^ ^
= ^ ^
# print(hex(s0)+':'+hex(s1)+':' + hex(R(w[j - 2], 17)))
# 初始化
, , , , , , , = , , , , , , ,
# for j in w:
# print(hex(j)[2:])
= ^ ^
= ^ ^
= +
= ^ ^
= ^
= + + + +
= & 0xffffffff
= & 0xffffffff
= & 0xffffffff
= & 0xffffffff
= & 0xffffffff
= & 0xffffffff
= & 0xffffffff
= & 0xffffffff
= & 0xffffffff
= & 0xffffffff
= & 0xffffffff
= & 0xffffffff
= & 0xffffffff
= & 0xffffffff
= & 0xffffffff
= & 0xffffffff
= | | |
|= | | |
# print(hex(digest)[2:]) # .rjust(32, '0'))
return # .rjust(32, '0')
=
Shell命令md5sum
|