# -*- coding: utf-8 -*- """ 纯 Python XTEA 密钥提取 回归测试 (AGENTS.md §5.9) 固化的性质: 1. **跨构建通用**: opcode 编号每份构建整体重排, 但仍能按构建推导出正确语义表; 2. **纯 Python == Node 神谕**: 6 份构建(含 1 份新抓的实时 tdc.js)的期望密钥 全部由 Node `extract_xtea_key` 逐字对照确认过, 此处固化为断言 (不再依赖 Node 跑测试); 3. **定位不写死 pc**: 断言覆盖 core_js / root / 1ms(旧+两份实时) / 实时新抓 共 6 份 不同构建, 任一份 opcode 重排或寄存器分配变化都会立刻暴露。 运行: .venv/bin/python -m tcaptcha.tests.test_tdc_key """ from __future__ import annotations import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[2] if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) from tcaptcha.tdc_key import extract_xtea_key_pure # noqa: E402 from tcaptcha.tdc_opcodes import derive_opcode_table # noqa: E402 # (相对路径, 期望有效密钥, 是否必须存在) # 期望值来自 Node 插桩 extract_xtea_key, 逐字一致。 # 入库样本 (core_js / root / 实时新抓 fixture) 为必需门禁; har/** 被 .gitignore 忽略, # 本地有这些样本时会一并回归, 缺少时自动跳过。 FIXTURES = [ ("tcaptcha/core_js/tdc_sample.js", [1818510928, 1381395306, 1245924197, 1768384072], True), ("tcaptcha/tdc_sample.js", [1448563793, 1850559083, 1733513813, 1364613714], True), # 2026-10-11 现场从 turing.captcha.qcloud.com 下发的 tdc.js (入库固化, 防"仅本地可验") ("tcaptcha/tests/fixtures/tdc_fresh_2026-10-11.js", [1397244740, 1799507557, 1399740484, 1516463189], True), ("har/1ms_dianxuan/tdc.js", [1448106085, 1766670948, 1819173482, 1298358636], False), ("har/1ms_dianxuan/live/tdc_live.js", [1314475846, 1500397933, 1751410275, 1716217423], False), ("har/1ms_dianxuan/live/tdc_latest.js", [1800883800, 1850828643, 1197042522, 1800823125], False), ] _fails: list = [] def check(name: str, cond: bool, detail: str = "") -> None: print(f" [{'PASS' if cond else 'FAIL'}] {name}" + (f" {detail}" if detail else "")) if not cond: _fails.append(name) def main() -> int: print("1) 按构建推导 opcode 表 (58 个 handler, 无未识别)") tables = {} skipped = [] for rel, _expect, required in FIXTURES: p = ROOT / rel if not p.exists(): if required: check(f"fixture {rel}", False, "缺失") else: skipped.append(rel) continue names, arity, dynamic, diag = derive_opcode_table(p.read_text(encoding="utf-8", errors="replace")) tables[rel] = names check(f"opcode 表 {Path(rel).name}", diag["handler_count"] == 58 and not diag["unknown_slots"], f"handlers={diag['handler_count']} unknown={diag['unknown_slots']}") if skipped: print(f" (跳过本地未提供的样本 {len(skipped)} 份: {[Path(s).name for s in skipped]})") print("\n2) opcode 编号确实每份构建都不同 (证明没有依赖写死的数值)") jump_ops = {rel: next(o for o, n in tabs.items() if n == "JUMP") for rel, tabs in tables.items()} if len(jump_ops) >= 2: check("JUMP 的 opcode 跨构建不统一", len(set(jump_ops.values())) >= 2, str(jump_ops)) print("\n3) 纯 Python 提取 == Node 神谕 (逐字)") for rel, expect, required in FIXTURES: p = ROOT / rel if not p.exists(): continue js = p.read_text(encoding="utf-8", errors="replace") try: rep = extract_xtea_key_pure(js) check(Path(rel).name, rep["key"] == expect, f"{rep['key']}") except Exception as exc: # noqa: BLE001 check(Path(rel).name, False, f"{type(exc).__name__}: {exc}") print("\n4) 密钥构造自洽: base + 折盐 == key") for rel, expect, required in FIXTURES: p = ROOT / rel if not p.exists(): continue rep = extract_xtea_key_pure(p.read_text(encoding="utf-8", errors="replace")) folded = list(rep["base"]) for idx, salt in rep["salts"]: folded[idx] = (folded[idx] + salt) & 0xFFFFFFFF check(f"折盐 {Path(rel).name}", folded == rep["key"] == expect) print() if _fails: print(f"❌ test_tdc_key 失败 {len(_fails)} 项: {_fails}") return 1 print("✅ test_tdc_key 全部通过") return 0 if __name__ == "__main__": raise SystemExit(main())