90 lines
2.4 KiB
Python
90 lines
2.4 KiB
Python
"""虎牙证书 nonce(rnd) 本地生成器。
|
||
|
||
基于 XXTEA 算法与 uid + k1 派生密钥。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import hashlib
|
||
import struct
|
||
import time
|
||
|
||
K1_DEFAULT = "865a4924a40897ac1fcfe6b4c2cbb0e3"
|
||
|
||
|
||
def _xxtea_encrypt_words(v: list[int], k: list[int]) -> list[int]:
|
||
"""标准 XXTEA (delta=0x9e3779b9, rounds=6+52/n)。"""
|
||
n = len(v)
|
||
if n < 2:
|
||
return v
|
||
z = v[n - 1]
|
||
y = v[0]
|
||
s = 0
|
||
delta = 0x9E3779B9
|
||
q = 6 + 52 // n
|
||
while q > 0:
|
||
s = (s + delta) & 0xFFFFFFFF
|
||
e = (s >> 2) & 3
|
||
p = 0
|
||
while p < n - 1:
|
||
y = v[p + 1]
|
||
z = (
|
||
v[p]
|
||
+ (
|
||
(((z >> 5) ^ (y << 2)) + ((y >> 3) ^ (z << 4)))
|
||
^ ((s ^ y) + (k[(p & 3) ^ e] ^ z))
|
||
)
|
||
) & 0xFFFFFFFF
|
||
v[p] = z
|
||
p += 1
|
||
y = v[0]
|
||
z = (
|
||
v[n - 1]
|
||
+ (
|
||
(((z >> 5) ^ (y << 2)) + ((y >> 3) ^ (z << 4)))
|
||
^ ((s ^ y) + (k[((n - 1) & 3) ^ e] ^ z))
|
||
)
|
||
) & 0xFFFFFFFF
|
||
v[n - 1] = z
|
||
q -= 1
|
||
return v
|
||
|
||
|
||
def xxtea_encrypt(data: bytes, key16: bytes) -> bytes:
|
||
"""虎牙专用 XXTEA: 输出 words = ceil(len/4)+1, 末尾 word 存原始长度。"""
|
||
n = len(data)
|
||
nwords = (n // 4) + 1
|
||
v = [0] * nwords
|
||
for i in range(n // 4):
|
||
v[i] = struct.unpack("<I", data[i * 4 : i * 4 + 4])[0]
|
||
v[nwords - 1] = n
|
||
k = [struct.unpack("<I", key16[i * 4 : i * 4 + 4])[0] for i in range(4)]
|
||
_xxtea_encrypt_words(v, k)
|
||
return b"".join(struct.pack("<I", w & 0xFFFFFFFF) for w in v)
|
||
|
||
|
||
def pack_u64(v: int) -> bytes:
|
||
"""8字节小端裸值。"""
|
||
return struct.pack("<Q", v & 0xFFFFFFFFFFFFFFFF)
|
||
|
||
|
||
def gen_nonce(
|
||
uid: int,
|
||
k1: str = K1_DEFAULT,
|
||
service_time_ms: int | None = None,
|
||
counter: int = 0,
|
||
) -> bytes:
|
||
"""按设备算法生成 20B nonce(rnd)。
|
||
|
||
- uid: 虎牙 uid 数字(如 1199666914671)
|
||
- k1: 设备常量 32hex
|
||
- service_time_ms: 缺省为当前毫秒时间戳
|
||
- counter: 计数器(通常为 0)
|
||
"""
|
||
st = service_time_ms if service_time_ms is not None else int(time.time() * 1000)
|
||
st &= 0x7FFFFFFFFFFF
|
||
nonce_val = (counter & 0xFFFF) | (st << 16)
|
||
data = pack_u64(st) + pack_u64(nonce_val)
|
||
key = hashlib.md5((str(uid) + k1).encode()).hexdigest()[:16].encode()
|
||
return xxtea_encrypt(data, key)
|