密码学算法之 SHA-3 keccak 算法

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/BlackNight168/article/details/82952489
参考链接:Keccak SHA-3 算法介绍----全新的 SHA-3 加密标准 —— Keccak
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 
  • 加入字符串处理的轮函数,内容包括上面的 Round 函数,是总轮函数
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
  • sha3 主体函数
# 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 源代码及官方文档

欢迎大家评论,踊跃发言
展开阅读全文

没有更多推荐了,返回首页