Author: John
sha3 哈希加密算法,不可逆
Keccak算法,sha家族最新算法,采用的不同于 MD(如md5) 结构的海绵结构(sponge结构),使常用于攻击 MD 结构的手段难以进行
增强了算法安全性
任意 hash 均可
FIPS 202 - SHA-3 Standard
填充主要过程是:明文转换成 16 进制,再转换成 2 进制,因为 utf8 编码明文后成 16 进制形式后,明文比特数至少是 4 的倍数,所以填充明文首先加 16 进制 06
然后,末尾加 80 中间填充 0,然后进行小端转换,再进行 hash ;
压缩主函数中每组明文,首轮加密前进行二维数组矩阵转置,之后进行 Round 函数加密,然后最后一轮加密后,再进行矩阵转置,相当于每组明文加密转置了两次,
或者某种程度上可以说没转置;
全部加密完成后,再进行小端转换得 sha3 hash 值。
注:1、最新 sha3 加密过程,并不包含 1 字节中 高位在后,低位在前 的规则,
如果非要说有此规则,那么就是 填充时,明文比特串先加后缀 01 再加 1 再加 若干 0 最后加 1,然后把填充的比特串按每字节 高位在后,低位在前 转换(即每字节二进制字符串逆置)
2、小端转换是 8 字节,类似于 C语言 long 类型转换
3、假如填充完一组明文后(明文总共一组 r-8 bit 明文,那么填充后明文后,需加 16 进制 86 此处是重点*******)
# sha3 轮函数
def Round(A, round_num): # A 为引用参数,相当于 c++ 的引用参数,A 二维数组实参值已改变
B = [[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0]]
C = [0, 0, 0, 0, 0]
D = [0, 0, 0, 0, 0]
# θ 变换
# 步骤一:将每列(y轴)压缩为 1,(异或),好像只有 x轴(行轴)和z轴(道轴),其中坐标轴为 x 朝右,y 朝上,z 朝里
for x in range(5):
C[x] = A[x][0] ^ A[x][1] ^ A[x][2] ^ A[x][3] ^ A[x][4]
# 步骤二:将某H点临近的两列(已压缩为两点)异或为D点,在第三步,该H点异或D点(数据)
for x in range(5):
D[x] = C[(x - 1) % 5] ^ ROT(C[(x + 1) % 5], 1)
# 步骤三:某H点异或D点
for x in range(5):
for y in range(5):
A[x][y] = A[x][y] ^ D[x]
# ρ 变换 and π 变换
for x in range(5):
for y in range(5):
B[y][(2 * x + 3 * y) % 5] = ROT(A[x][y], P[x][y])
# χ 变换
for x in range(5):
for y in range(5):
A[x][y] = B[x][y] ^ ((~B[(x + 1) % 5][y]) & B[(x + 2) % 5][y])
# τ 变换
A[0][0] = A[0][0] ^ (RC[round_num]%(1<<W))
print('第'+str(round_num)+'轮输出:')
for x in range(5):
for y in range(5):
print('最终A:A[%d][%d]:%s\t' % (x, y, hex(A[x][y])),end='\t')
print()
return A
def Keccak_wheel_f(strbin_plain, round_num):
# 将 1600 bit 转换成 5行 5列 二维数组 每个元素 64 bit 二进制字符串转化成整型的列表
listplain = [[] for i in range(5)]
k=0 # 分为 k 组 5 组
for i in range(25):
if i % 5==0 and i!=0:
k+=1
temp=strbin_plain[W * i:W * (i + 1)]
listplain[k].append(int(temp,2))
# 将 A_init 赋值为 二维整型数组
A_init=listplain
# 加密初始时,对 5 行 5 列数组行列转置
if round_num == 0:
A_init = exchange_xy(A_init)
# 执行轮函数
A_end = Round(A_init, round_num)
# 一组明文加密结束,再将此二维数组转置回来
if round_num == 23:
A_end = exchange_xy(A_end)
# 将 A_end 转为 1600 bit 二进制字符串
strcipher = ''
for x in range(5):
for y in range(5):
strtemp = bin(A_end[x][y])[2:]
strcipher += fillup_16_or_2_to_normal(strtemp, 64)
return strcipher
# Keccak 主要函数 输入 如:'111001...' 输出:'0b10011...'
def Keccak_f_1600(plaintext_2, result_length=512):
r = 576
b = 1600
# θρπχτ
# 填充函数
j = (-len(plaintext_2) - 2) % r
if j == 6:
plaintext_2 += '10000110'
else:
plaintext_2 += '00000110' + '0' * (j - 14) + '10000000' # 需要逆置
# 输出16进制 1600 bit 分组填充后字符串
for i in range(len(plaintext_2) // r):
plaintext_16 = hex(int(plaintext_2[i * r:(i + 1) * r], 2))
print('填充后第 %d 组化成 16 进制为 %s ,16 进制串长度为 %d' % (i, plaintext_16, len(plaintext_16)))
# 小端转换
plaintext_2_small = groups_64_split(plaintext_2)
print('小端转换后为(注意 0x 后可能有 0 省略):', hex(int(plaintext_2_small, 2)), \
'其 16 进制长:', len(hex(int(plaintext_2_small, 2))),'其 2 进制长:', len(plaintext_2_small))
# 将二进制字符串 分为 num 段
num =len(plaintext_2_small) // r
c = b - r
p2_list = [''] * num
# 明文分成 num 组
for i in range(num):
p2_list[i] = plaintext_2_small[r * i:r * (i + 1)]
# 吸收阶段
# 将 s 前添加 '000' 使其长度为 b
s = '0' * b
for i in range(num):
s = bin_xor(s, p2_list[i] + '0' * c)
# 每组明文进行加密
for j in range(24):
s = Keccak_wheel_f(s, j)
# 挤压阶段
z = '' # z 包含所有加密后的密文
while True:
# 截取 密文
z = z + s[:r]
if result_length <= len(z):
break
else:
for j in range(24):
s = Keccak_wheel_f(s,j) # 继续加密,输出
return z[:result_length]
本代码实现算法与 hashlib 接口实现的 hash 值完全相同,欢迎检测,检测方法如下:
import hashlib
plaintext='123456' # 所要 hash 的明文字符串
m = hashlib.sha3_512()
m.update(plaintext.encode('utf-8'))
print('sha3_512加密结果:', m.hexdigest())
全部代码下载链接:SHA-3 哈希摘要算法 python 源代码及官方文档