Files

91 lines
2.4 KiB
Python

"""虎牙 udb 登录证书 (biz_token) 铸造与解析工具。
证书格式:base64( [0x0c][key_idx][AES-128-ECB(key16, zeropad(P1))] )
"""
from __future__ import annotations
import os
import struct
from .udb_aes import udb_decrypt, udb_encrypt
KEY_TABLE_RAW = [
"4VYcPdvKKqjBHZtCmbroRXHk",
"xXEDWqiKLGwEZ6HubEiswCqK",
"3FMHubdKosFrhmXNLHTNHZwe",
]
DEFAULT_KEY16 = KEY_TABLE_RAW[0][:16].encode()
def build_p1(
app_id: bytes,
fingerprint: bytes,
cred: bytes,
rnd: bytes | None = None,
) -> bytes:
"""按样本结构组装 P1 明文。
- app_id: b'5008' 等 4 字节
- fingerprint: 40 字节 ASCII hex
- cred: 114 字节 hyCred
- rnd: 20 字节 nonce,缺省用 os.urandom(20)
"""
assert len(fingerprint) == 40, f"指纹应为40B, 实际为 {len(fingerprint)}"
assert len(cred) == 114, f"cred应为114B, 实际为 {len(cred)}"
r = os.urandom(20) if rnd is None else rnd
p1 = b"\x01\x04\x00" + app_id
p1 += struct.pack("<H", len(r)) + r
p1 += struct.pack("<H", len(fingerprint)) + fingerprint
p1 += struct.pack("<H", len(cred)) + cred
assert len(p1) == 187, f"P1长度应为187,实际为 {len(p1)}"
return p1
def parse_p1(data: bytes) -> dict:
"""解析 P1 结构。"""
o = 0
def tk(n: int) -> bytes:
nonlocal o
b = data[o : o + n]
o += n
return b
out = {
"head": tk(3).hex(),
"app_id": tk(4),
}
n = struct.unpack("<H", tk(2))[0]
out["rnd"] = tk(n)
n = struct.unpack("<H", tk(2))[0]
out["fingerprint"] = tk(n)
n = struct.unpack("<H", tk(2))[0]
out["cred"] = tk(n)
out["trailing"] = data[o:]
return out
def forge_cert(
p1: bytes,
key16: bytes = DEFAULT_KEY16,
key_idx: int = 0x20,
type_byte: int = 0x0C,
) -> bytes:
"""P1 -> 二进制证书 [type][key_idx][192B AES 密文]。"""
ct = udb_encrypt(key16, p1)
assert len(ct) == 192, len(ct)
return bytes([type_byte, key_idx]) + ct
def parse_cert(blob: bytes) -> tuple[int, int, bytes]:
"""[0x0c][idx][192B] -> (type, idx, ct)。"""
assert blob[0] == 0x0C and len(blob) >= 194, "非证书结构"
return blob[0], blob[1], blob[2:194]
def decrypt_cert(blob: bytes, key16: bytes = DEFAULT_KEY16) -> bytes:
"""解密二进制证书为 P1 明文。"""
_, _, ct = parse_cert(blob)
return udb_decrypt(key16, ct)