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

273 lines
8.4 KiB
Markdown

# 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**:
```python
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:
```python
# 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
```python
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
### 1. Login to Obtain Cookie
```bash
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
```python
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)
```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
```python
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)
```python
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
```python
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)
```
---
## Legal Notice
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.