统一虎牙 App 登录设备画像
This commit is contained in:
@@ -8,23 +8,24 @@
|
||||
* 失败语义: 模板缺失/HTTP 异常 -> DfpRegistrationError (不静默、不发登录帧)。
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import base64
|
||||
import json
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
import hashlib
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
import requests
|
||||
|
||||
from core.huya.dfp_register import (
|
||||
CHAIN_FILE,
|
||||
CW_LEN,
|
||||
SELECT_OPERATOR_SDID,
|
||||
DfpRegistrationError,
|
||||
_build_dfp_json_plain,
|
||||
_build_get_dfp_config_request,
|
||||
_build_random_dfp_body,
|
||||
_build_select_operator_request,
|
||||
_parse_response,
|
||||
register_device,
|
||||
)
|
||||
from core.huya.wup_protocol import WupResponse
|
||||
|
||||
TAIL = bytes.fromhex("3600400c0b8c980ca80c")
|
||||
|
||||
@@ -59,6 +60,40 @@ class TestRandomDfpBody:
|
||||
b2 = _build_random_dfp_body()
|
||||
assert b1 != b2, "每次注册应生成不同的随机 cw"
|
||||
|
||||
def test_body_uses_device_profile_fields(self):
|
||||
profile = {
|
||||
"app_version": "13.4.22",
|
||||
"vendor": "vivo",
|
||||
"model": "V2370A",
|
||||
"screen": "V2370A,34,13",
|
||||
"width": "1080",
|
||||
"height": "2412",
|
||||
"device_id": "d" * 64,
|
||||
"guid32": "g" * 64,
|
||||
}
|
||||
with (
|
||||
patch(
|
||||
"core.huya.dfp_register._random_triple",
|
||||
return_value=("h" * 64, "r" * 64, "k" * 64),
|
||||
),
|
||||
):
|
||||
plaintext = _build_dfp_json_plain(profile)
|
||||
assert b'"channel":"vivo"' in plaintext
|
||||
assert b'"deviceName":"V2370A"' in plaintext
|
||||
assert b'"deviceId":"' + b"d" * 64 in plaintext
|
||||
assert b'"systemVer":"V2370A,34,13"' in plaintext
|
||||
with (
|
||||
patch(
|
||||
"core.huya.dfp_register._random_triple",
|
||||
return_value=("h" * 64, "r" * 64, "k" * 64),
|
||||
),
|
||||
patch(
|
||||
"core.huya.dfp_register.os.urandom", side_effect=lambda n: b"\0" * n
|
||||
),
|
||||
):
|
||||
body = _build_random_dfp_body(profile)
|
||||
assert hashlib.sha256(plaintext).digest() in body[-CW_LEN + 2 :]
|
||||
|
||||
|
||||
class TestParseResponse:
|
||||
def test_parse_ok(self):
|
||||
@@ -67,17 +102,6 @@ class TestParseResponse:
|
||||
assert t2 == "A" * 180
|
||||
assert t5 == "b" * 40
|
||||
|
||||
def test_parse_golden_evidence(self):
|
||||
"""真实抓包模板响应必须可解析 (证据: evidence/dfp_chain_golden.json)。"""
|
||||
if not CHAIN_FILE.exists():
|
||||
pytest.skip("缺失 golden 注册链模板")
|
||||
data = json.loads(CHAIN_FILE.read_text(encoding="utf-8"))
|
||||
resp = base64.b64decode(data["dfpReport"]["resp_b64"])
|
||||
t1, t2, t5 = _parse_response(resp)
|
||||
assert len(t1) == 32
|
||||
assert len(t2) == 180
|
||||
assert len(t5) == 40
|
||||
|
||||
def test_parse_missing_fields_raises(self):
|
||||
with pytest.raises(DfpRegistrationError):
|
||||
_parse_response(b"\x16\x20" + b"a" * 32) # 缺 t2/t5
|
||||
@@ -88,6 +112,27 @@ class TestParseResponse:
|
||||
|
||||
|
||||
class TestRegisterDevice:
|
||||
def test_get_dfp_config_request_is_generated(self):
|
||||
req = _build_get_dfp_config_request()
|
||||
assert len(req) == 67
|
||||
wup = WupResponse()
|
||||
wup.decode(req)
|
||||
assert wup.sServantName == "huyaudbwebui"
|
||||
assert wup.sFuncName == "getDfpConfig"
|
||||
assert wup.readStruct("tReq") == bytes.fromhex("0a0604353030380b")
|
||||
|
||||
def test_select_operator_request_is_generated(self):
|
||||
fp = "0" * 40
|
||||
req = _build_select_operator_request(fp)
|
||||
assert len(req) == 419
|
||||
assert fp.encode() in req
|
||||
assert SELECT_OPERATOR_SDID.encode() in req
|
||||
wup = WupResponse()
|
||||
wup.decode(req)
|
||||
assert wup.sServantName == "huyaudbwebui"
|
||||
assert wup.sFuncName == "selectOperator"
|
||||
assert "_wup_data" in wup.newdata
|
||||
|
||||
def test_flow_three_steps_and_returns_triple(self):
|
||||
calls = []
|
||||
|
||||
@@ -134,19 +179,6 @@ class TestRegisterDevice:
|
||||
assert len(dfp_body) == 4226
|
||||
assert dfp_ct == "application/octet-stream"
|
||||
|
||||
def test_chain_template_missing_raises(self):
|
||||
"""模板缺失 -> 注册链直接报错, 不应发起任何网络请求。"""
|
||||
with (
|
||||
patch(
|
||||
"core.huya.dfp_register.CHAIN_FILE",
|
||||
Path("/tmp/definitely_missing_chain.json"),
|
||||
),
|
||||
patch("core.huya.dfp_register._post") as m_post,
|
||||
):
|
||||
with pytest.raises(DfpRegistrationError):
|
||||
register_device()
|
||||
m_post.assert_not_called()
|
||||
|
||||
def test_http_failure_raises(self):
|
||||
"""HTTP 层异常由 _post 转换为 DfpRegistrationError 后向上传播 (不静默)。"""
|
||||
with (
|
||||
@@ -155,6 +187,6 @@ class TestRegisterDevice:
|
||||
"core.huya.dfp_register.requests.post",
|
||||
side_effect=requests.RequestException("connection reset"),
|
||||
),
|
||||
pytest.raises(DfpRegistrationError),
|
||||
):
|
||||
with pytest.raises(DfpRegistrationError):
|
||||
register_device()
|
||||
register_device()
|
||||
|
||||
Reference in New Issue
Block a user