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 总体框架

单个块 X1·Y1 乘法定义

测试向量
数据源自 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()}")


浙公网安备 33010602011771号