Files
hunyuan3dweb/README_REVERSE_ENGINEERING.md
Claude 58ab0d6655 docs: update reverse engineering manuals with latest findings
- Add cos_upload.py pure-Python COS upload method (no browser needed)
- Expand API endpoint table to include all discovered endpoints
- Document dual-verification login detection (URL + DOM)
- Add COS V1 signing algorithm explanation
- Update file reference table to match current codebase
- Remove obsolete file references (get_resource_id.py, etc.)
- Sync Chinese and English versions

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 03:43:39 +08:00

8.4 KiB

Tencent Hunyuan 3D API Reverse Engineering Document

Project Overview

This project reverse-engineers the frontend signing algorithm of Tencent Hunyuan 3D (https://3d.hunyuan.tencent.com/), enabling pure Python HTTP calls without a browser environment for features like image-to-3D generation and quota queries.


Core Achievements

1. Signing Algorithm Cracked

Algorithm Location: webpack Chunk 3057, Module 47436

Signing Flow:

1. nonce generation: 16 iterations of Math.random(), selecting from 62-char set (A-Za-z0-9)
2. timestamp: Math.floor(Date.now() / 1000) (second-level timestamp)
3. key derivation: hardcoded byte array → XOR → circular left shift → permutation → truncation
4. signature: HMAC-SHA256(sorted query param string, derived key) → Hex

Key Constants:

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]  # left shift bits
M = [14, 11, 13, 9, 15, 10, 12, 8, 6, 3, 5, 1, 7, 2, 4, 0]  # permutation table

Derived Key: Hf6d6KFB3D (10 characters)

2. Signing Scope (Important Finding)

⚠️ The signature only covers URL query parameters, NOT the request body

Actual browser request:

POST /api/3d/creations/generations?timestamp=xxx&nonce=yyy&sign=zzz
Body: {"sceneType":"playGround3D-2.0",...}

Signature computation:

# Only sign query params (timestamp + nonce)
param_str = "nonce=yyy&timestamp=xxx"
sign = HMAC-SHA256(param_str, key="Hf6d6KFB3D")

3. Image Upload Evolution

Method 1: Browser Automation Upload (Original)

Upload via generator.py inside the browser to obtain a Tencent internal resourceId.

Method 2: Pure Python COS Direct Upload (New)

Upload directly via cos_upload.py without a browser:

  1. Call /resource/genUploadInfo to obtain temporary STS credentials (encryptTmpSecretId / encryptTmpSecretKey / encryptToken)
  2. Generate the Authorization header using the COS V1 signing algorithm
  3. PUT directly to *.cos.accelerate.myqcloud.com
  4. Obtain resourceUrl for subsequent image-to-3D API calls
from hunyuan3dweb.cos_upload import upload_image

resource_url = upload_image("/path/to/image.png")

File Reference

File Description
hunyuan3dweb/sign.py Signing algorithm in pure Python
hunyuan3dweb/api.py Basic API client (image2model, text2model)
hunyuan3dweb/api_complete.py Full API client (all generation modes)
hunyuan3dweb/cos_upload.py Pure Python COS upload helper (no browser needed)
hunyuan3dweb/config.py User config path management (cookies, profile)
hunyuan3dweb/cli.py CLI entry point
hunyuan3dweb/browser/login.py Browser automation login tool
hunyuan3dweb/browser/generator.py Browser automation image-to-3D
hunyuan3dweb/browser/sniffer.py API request sniffer

Usage Flow

hunyuan3dweb-login
# Follow prompts to enter email and verification code
# Login state is automatically saved to ~/.config/hunyuan3dweb/profile
# Cookies are also exported to ~/.config/hunyuan3dweb/cookies.txt

Login State Detection Logic (dual verification):

  • Check if the current page URL contains "login"
  • Check if a "Login" button exists on the page
  • Only considered logged in when URL does NOT contain login AND no login button is present

2. Pure Python 3D Generation

from hunyuan3dweb import Hunyuan3DAPI

# Automatically load cookie from ~/.config/hunyuan3dweb/cookies.txt
api = Hunyuan3DAPI()

# Check quota
quota = api.get_quota_info()

# Text-to-3D
result = api.generate_text("a cute panda", title="Panda Model")

# Query status
status = api.get_generation_status(result["creationsId"])

3. Local Image Upload + Image-to-3D (Pure Python)

from hunyuan3dweb.cos_upload import upload_image
from hunyuan3dweb import Hunyuan3DAPIComplete

# Upload local image to COS
resource_url = upload_image("/path/to/image.png")

# Call image-to-3D API
api = Hunyuan3DAPIComplete()
result = api.generate_from_image(resource_url, title="My Model")

API Endpoints

Feature Method Endpoint Signature Scope
User info GET /getuserinfo Query params
Quota query POST /quotainfo Query params
Generate 3D POST /creations/generations Query params
Query status GET /creations/detail Query params
Creation list POST /creations/list Query params
Creation count POST /creations/count Query params
Cancel generation POST /creations/cancel Query params
Upload credentials POST /resource/genUploadInfo Query params
Resource review POST /resource/review Query params
Create share POST /share Query params
Global config GET /config Query params
Animation templates GET /workflow/action/templates Query params

Technical Details

Signing Algorithm Implementation

def derive_key(c: bytes) -> str:
    """Derive signing key from hardcoded constants"""
    # 1. XOR with D
    t = bytearray(16)
    for i in range(16):
        t[i] = c[i] ^ D[i]

    # 2. Circular left shift
    o = bytearray(16)
    for i in range(16):
        n = U[i]
        val = t[i]
        o[i] = (val << n | val >> (8 - n)) & 0xFF

    # 3. Permutation
    n = bytearray(16)
    for i in range(16):
        n[i] = o[M[i]]

    # 4. Truncate (find first zero byte)
    try:
        r = n.index(0)
    except ValueError:
        r = 16

    return n[:r].decode('utf-8')

COS V1 Signature (for Direct Upload)

def _cos_v1_auth(method, pathname, query_params, headers, secret_id, secret_key, key_time):
    sign_key = hmac.new(secret_key.encode("utf-8"), key_time.encode("utf-8"), hashlib.sha1).hexdigest()

    query_str = _obj2str(query_params, True)
    headers_str = _obj2str(headers, True)
    format_string = f"{method}\n{pathname}\n{query_str}\n{headers_str}\n"

    format_string_sha1 = hashlib.sha1(format_string.encode("utf-8")).hexdigest()
    string_to_sign = f"sha1\n{key_time}\n{format_string_sha1}\n"

    signature = hmac.new(sign_key.encode("utf-8"), string_to_sign.encode("utf-8"), hashlib.sha1).hexdigest()

    return "&".join([
        "q-sign-algorithm=sha1",
        f"q-ak={secret_id}",
        f"q-sign-time={key_time}",
        f"q-key-time={key_time}",
        f"q-header-list={';'.join(sorted(headers.keys())).lower()}",
        f"q-url-param-list={';'.join(sorted(query_params.keys())).lower()}",
        f"q-signature={signature}",
    ])

Request Signing

def sign(params: dict) -> dict:
    result = dict(params)
    result["timestamp"] = int(time.time())
    result["nonce"] = generate_nonce(16)

    # Sort and join (JSON format for lists/dicts)
    sorted_items = sort_params(result)
    param_str = join_params(sorted_items)

    # HMAC-SHA256
    key = derive_key(C)
    signature = hmac.new(
        key.encode('utf-8'),
        param_str.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()

    result["sign"] = signature
    return result

Verification Results

Test Item Browser Signature Python Signature Match
Fixed params 2214e55a... 2214e55a...
Quota query Works Works
Generate request Works Works
Status query Works Works
COS direct upload Browser upload Python upload

Limitations and Notes

  1. Cookie Expiration: Login sessions expire and need periodic re-login
  2. Quota Limit: Each account has a generation quota limit (default 20/day)
  3. Rate Limiting: Frequent calls may trigger anti-bot measures
  4. Concurrency Safety: When multiple tools run simultaneously, login.py exclusively uses the standard profile directory, while sniffer.py / generator.py automatically copy the profile to a temporary directory to avoid Chromium SingletonLock conflicts

Dependencies

requests
cloakbrowser (for login and browser automation)

This project is for educational and research purposes only. Use of this code is subject to Tencent Hunyuan 3D's Terms of Service. Do not use for commercial purposes or large-scale automated calling.