Initial commit

This commit is contained in:
2026-05-24 21:47:44 +08:00
commit 2ce35563c4
20 changed files with 3698 additions and 0 deletions
+162
View File
@@ -0,0 +1,162 @@
#!/usr/bin/env python3
"""
腾讯混元3D签名算法实现
从 webpack 模块逆向提取
"""
import hmac
import hashlib
import time
import random
import string
# 密钥派生相关常量(从 JS 提取)
C = bytes([122, 59, 92, 165, 30, 79, 166, 139, 142, 129, 139, 89, 219, 131, 101, 204])
D = bytes([122, 59, 92, 45, 30, 79, 106, 139, 156, 13, 46, 63, 74, 91, 108, 125])
U = [3, 5, 2, 7, 1, 4, 6, 2, 5, 3, 1, 4, 2, 6, 3, 5]
M = [14, 11, 13, 9, 15, 10, 12, 8, 6, 3, 5, 1, 7, 2, 4, 0]
def derive_key(c: bytes) -> str:
"""密钥派生函数 - 从硬编码常量派生签名密钥"""
if len(c) != 16:
raise ValueError("输入必须是一个16字节的数组")
# 步骤1: XOR with D
t = bytearray(16)
for i in range(16):
t[i] = c[i] ^ D[i]
# 步骤2: 循环左移
o = bytearray(16)
for i in range(16):
n = U[i]
# Python 的移位和 JS 不同,需要模拟 8 位无符号整数
val = t[i]
o[i] = (val << n | val >> (8 - n)) & 0xFF
# 步骤3: 置换
n = bytearray(16)
for i in range(16):
n[i] = o[M[i]]
# 步骤4: 找到第一个0字节,截断
try:
r = n.index(0)
except ValueError:
r = 16
return n[:r].decode('utf-8')
def generate_nonce(length: int = 16) -> str:
"""生成随机 nonce"""
chars = string.ascii_letters + string.digits # 62字符
return ''.join(random.choice(chars) for _ in range(length))
def get_timestamp() -> int:
"""获取当前时间戳(秒)"""
return int(time.time())
def sort_params(params: dict) -> list:
"""排序参数,过滤空值"""
import json
items = []
for k, v in params.items():
if v is not None and v != "":
# 对列表和字典使用 JSON 格式
if isinstance(v, (list, dict, bool)):
items.append((k, json.dumps(v, separators=(',', ':'), ensure_ascii=False)))
else:
items.append((k, str(v)))
return sorted(items, key=lambda x: x[0])
def join_params(items: list) -> str:
"""拼接参数为查询字符串"""
return '&'.join(f"{k}={v}" for k, v in items)
def sign(params: dict,
nonce_length: int = 16,
timestamp_field: str = "timestamp",
nonce_field: str = "nonce",
sign_field: str = "sign") -> dict:
"""
主签名函数
Args:
params: 请求参数
nonce_length: nonce 长度
timestamp_field: 时间戳字段名
nonce_field: nonce 字段名
sign_field: 签名字段名
Returns:
包含签名的新参数字典
"""
result = dict(params)
result[timestamp_field] = get_timestamp()
result[nonce_field] = generate_nonce(nonce_length)
# 排序并拼接
sorted_items = sort_params(result)
param_str = join_params(sorted_items)
# 派生密钥
key = derive_key(C)
# HMAC-SHA256 签名,然后转为 Hex
signature = hmac.new(
key.encode('utf-8'),
param_str.encode('utf-8'),
hashlib.sha256
).hexdigest()
result[sign_field] = signature
return result
def sign_with_custom_nonce(params: dict, timestamp: int, nonce: str,
timestamp_field: str = "timestamp",
nonce_field: str = "nonce",
sign_field: str = "sign") -> dict:
"""使用指定的 timestamp 和 nonce 生成签名(用于验证)"""
result = dict(params)
result[timestamp_field] = timestamp
result[nonce_field] = nonce
sorted_items = sort_params(result)
param_str = join_params(sorted_items)
key = derive_key(C)
signature = hmac.new(
key.encode('utf-8'),
param_str.encode('utf-8'),
hashlib.sha256
).hexdigest()
result[sign_field] = signature
return result
if __name__ == "__main__":
# 测试密钥派生
key = derive_key(C)
print(f"派生密钥: {key}")
print(f"密钥长度: {len(key)}")
# 测试签名
test_params = {
"sceneType": "playGround3D-2.0",
"count": 1,
"modelType": "image2ModelV3.1"
}
signed = sign(test_params)
print(f"\n测试签名:")
for k, v in sorted(signed.items()):
print(f" {k}: {v}")