GHASH(input_data,H) 函数的Python实现

背景介绍

\(GHASH_H\) 是 NIST规范 https://csrc.nist.gov/pubs/sp/800/38/d/final 中的数学组件,用于描述GCM模式块加密。

· 为定义在二元伽罗华域 的乘法,
H为 hash subkey,值为 分组加密 全零的结果, \(H = Enc_K(00...0_{128})\), 其中K为用户的密钥(如AES密钥)。
常数R 为 \(11100001 || 0^{120}\)
实际伽罗华乘法的取模多项式(128阶)为: \(11100001 || 0^{120} || 1 == R || 1\)

GHASH_H( X ) -> Y 总体框架

image

单个块 X1·Y1 乘法定义

image

测试向量

数据源自 NIST官方GCM测试数据集

Key = af57f42c60c0fc5a09adb81ab86ca1c3
IV = a2dc01871f37025dc0fc9a79
CT = b9a535864f48ea7b6b1367914978f9bfa087d854bb0e269bed8d279d2eea1210e48947338b22f9bad09093276a331e9c79c7f4
AAD = 41dc38988945fcb44faf2ef72d0061289ef8efd8
Tag = 4f71e72bde0018f555c5adcce062e005
PT = 3803a0727eeb0ade441e0ec107161ded2d425ec0d102f21f51bf2cf9947c7ec4aa72795b2f69b041596e8817d0a3c16f8fadeb
---
H = Ek( 0^128) = 67FE72A012F8DD231E85A9A93F339656

代码实现

def ghash_h(input_data: bytes, H_key: bytes) -> bytes:
    """
    Python实现GCM模式中的GHASH函数(符合RFC 4494/NIST SP 800-38D)
    
    参数:
    input_data: bytes - 输入数据字节序列
    H_key: bytes      - 128位GHASH密钥(16字节)
    
    返回:
    bytes - 16字节的GHASH计算结果
    """
    if len(H_key) != 16:
        raise ValueError("H_key必须是16字节长度")
    
    # 常数R: 11100001 || 0(^120) 
    # 此域的本原多项式为 x^128 + x^7 + x^2 + x + 1,
    # 所以在此GF域有等式-x^128 = x^7 + x^2 + x^1 + 1
    # 采用一个工具系数R, 使得R = -x^128,即R = 11100001 || 0(^120)
    R = 0xE1000000000000000000000000000000
    
    def gmul_128(Y1: int, Y2: int) -> int:
        """GF(2^128)乘法函数"""
        z = 0
        v = Y2
        # 遍历所有128位 对应多项式 x^127 ... x^1的系数
        # Y1 = c0 + c1*x^1 + ... + c127*x^127
        # Y2 = d0 + d1*x^1 + ... + d127*x^127
        # 对下方循环的解释:Y1·Y2 = c0*Y + c1*x*Y + ... + c127*x^127*Y
        for i in range(127, -1, -1):
            # 检查Y1的左起第127-i位是否为1,1则做GF(2^128)加法
            if (Y1 >> i) & 1:
                z ^= v
            
            # 此步骤为下一循环的被加数做准备
            # 检查v的最后一比特(对应多项式的最高项系数)
            carry = v & 1
            # 右移意味着试着给Y多项式升幂,最低比特对应x^127升高到x^128
            # 而x^128认为是-R或者说+R,转化为加R(异或)即可约减到域内
            v = v >> 1
            if carry:
                v ^= R
        return z
    
    # 将输入数据分割为16字节块
    blocks = []
    for i in range(0, len(input_data), 16):
        block = input_data[i:i+16]
        # 不足16字节时右对齐填充0
        if len(block) < 16:
            block = bytes(16 - len(block)) + block
        # 转换为大端序整数
        block_int = int.from_bytes(block, 'big')
        blocks.append(block_int)
    
    # GHASH计算过程
    H = int.from_bytes(H_key, 'big')
    Y = 0
    for block in blocks:
        Y = gmul_128(Y ^ block, H)
    
    # 返回大端序字节序列
    return Y.to_bytes(16, 'big')

def hex_to_bytes(hex_str: str) -> bytes:
    """将十六进制字符串转换为字节序列
    
    :param hex_str: 十六进制字符串,可包含空格和0x前缀
    :return: 字节序列(bytes)
    
    示例:
        >>> hex_to_bytes("48656c6c6f")  # "Hello" 的十六进制表示
        b'Hello'
        >>> hex_to_bytes("0x48 0x65 0x6c 6c 6f")
        b'Hello'
    """
    # 移除空格和0x前缀
    cleaned_hex = hex_str.replace(" ", "").replace("0x", "")
    
    # 确保十六进制字符串长度是偶数
    if len(cleaned_hex) % 2 != 0:
        raise ValueError("十六进制字符串长度必须为偶数")
    
    # 转换为字节序列
    return bytes.fromhex(cleaned_hex)

# 测试代码
if __name__ == "__main__":
    # 示例: 基本测试(使用预设H和输入dataX)
    h_str = '67FE72A012F8DD231E85A9A93F339656'
    # 输入数据X  :A|| 0..|| C|| 0.. || len(A) || len(C)
    x_str = '''
    41dc38988945fcb44faf2ef72d0061289ef8efd8000000000000000000000000
    b9a535864f48ea7b6b1367914978f9bfa087d854bb0e269bed8d279d2eea1210e48947338b22f9bad09093276a331e9c79c7f400000000000000000000000000
    00000000000000a00000000000000198
    '''
    X = hex_to_bytes(x_str)
    H = hex_to_bytes(h_str)
    
    digest = ghash_h(X, H)
    print(f"摘要参数H: {h_str}")
    print(f"测试输入X: {x_str}")
    print(f"GHASH摘要结果GHASH(H,X): {digest.hex()}")

image

posted @ 2025-07-08 18:22  北壹  阅读(173)  评论(0)    收藏  举报