mirror of
https://github.com/qtmleap/revkit.git
synced 2026-10-31 13:11:51 +01:00
feat(crypto): add compute_apphmac() and IOS_APPID/IOS_APPKEYVERSION constants
AppleWebCrypto::HKDF を再実装した compute_apphmac() を NetflixCrypto に追加。 Extract: prk = HMAC-SHA256(key=MGK, data=PSK) Expand: okm = HMAC-SHA256(key=prk, data=Nonce) - crypto.py: NetflixCrypto.compute_apphmac(enc_key_0, sign_key_0) → 32B apphmac - constants.py: IOS_APPID, IOS_APPKEYVERSION を追加 - ios_client.py: apphmac フィールドのドキュメントを更新、IOS_APPKEYVERSION をデフォルト値に使用 - tools/verify_full_key_chain.py: apphmac テストベクタを追加 (9/9 PASS) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
7cd95f6631
commit
7ca4183a08
@@ -64,6 +64,10 @@ IOS_DH_G = 5
|
||||
# appboot エンドポイント
|
||||
IOS_APPBOOT_ENDPOINT = "https://appboot.netflix.com/appboot/"
|
||||
|
||||
# iOS アプリ識別子 (NFWebCrypto.framework から抽出)
|
||||
IOS_APPID = "a2becfec-b286-535c-b884-903a384caee6"
|
||||
IOS_APPKEYVERSION = 1
|
||||
|
||||
# iOS appboot 署名検証鍵 (NFWebCrypto.framework からの静的抽出)
|
||||
# kAppBootKey: RSA-4096 SPKI/DER (550B) — RSASSA-PKCS1-v1_5 検証用
|
||||
# バイナリオフセット: NFWebCrypto @ 0x0020cd31 (__TEXT.__cstring, Base64)
|
||||
|
||||
@@ -457,6 +457,27 @@ class NetflixCrypto:
|
||||
sign_key = digest[16:48]
|
||||
return enc_key, sign_key
|
||||
|
||||
# ---- apphmac 計算 (FAIRPLAY_MGK_APPID entity_auth_data) ----
|
||||
|
||||
@staticmethod
|
||||
def compute_apphmac(enc_key_0: bytes, sign_key_0: bytes) -> bytes:
|
||||
"""apphmac を HKDF(key=MGK, ikm=PSK, info=Nonce) で計算する.
|
||||
|
||||
AppleWebCrypto::HKDF の再実装:
|
||||
Extract: prk = HMAC-SHA256(key=MGK, data=PSK)
|
||||
Expand: okm = HMAC-SHA256(key=prk, data=Nonce)
|
||||
|
||||
Args:
|
||||
enc_key_0: Phase 0 MGK 暗号化鍵 (16 bytes)
|
||||
sign_key_0: Phase 0 MGK 署名鍵 (32 bytes)
|
||||
|
||||
Returns:
|
||||
32 バイトの apphmac 値
|
||||
"""
|
||||
mgk = enc_key_0 + sign_key_0 # 48B
|
||||
prk = hmac_mod.new(mgk, IOS_KDF_PSK, hashlib.sha256).digest()
|
||||
return hmac_mod.new(prk, IOS_KDF_NONCE, hashlib.sha256).digest()
|
||||
|
||||
# ---- key 33.6 scheme_data 構築 (Scheme 3 / appboot) ----
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -32,7 +32,7 @@ appboot から MSL セッション鍵確立までの End-to-end フローを実
|
||||
|
||||
- enc_key_0 / sign_key_0: TFIT エミュレーション (emulate_tfit.py) で導出可能
|
||||
- devicetoken: Frida/Tweak `AppbootKeyExtract` でキャプチャ
|
||||
- apphmac: Frida/Tweak `AppbootKeyExtract` でキャプチャ
|
||||
- apphmac: NetflixCrypto.compute_apphmac(enc_key_0, sign_key_0) で計算可能
|
||||
- device_key_data: Frida/Tweak `AppbootKeyExtract` でキャプチャ (~6,576 bytes)
|
||||
- session_region: TFIT エミュレーション (emulate_tfit.py) または Frida キャプチャ
|
||||
- s1, s2, s3: Frida キャプチャで取得
|
||||
@@ -48,7 +48,7 @@ import requests
|
||||
|
||||
from netflix_msl.cbor_decoder import CborMslDecoder
|
||||
from netflix_msl.cbor_encoder import CborMslEncoder
|
||||
from netflix_msl.constants import IOS_APPBOOT_ENDPOINT
|
||||
from netflix_msl.constants import IOS_APPBOOT_ENDPOINT, IOS_APPID, IOS_APPKEYVERSION
|
||||
from netflix_msl.crypto import NetflixCrypto, SessionKeys
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ class iOSAppbootParams:
|
||||
|
||||
apphmac: str
|
||||
"""アプリ認証 HMAC-SHA256 (hex string).
|
||||
TODO: Frida/Tweak AppbootKeyExtract でキャプチャ."""
|
||||
NetflixCrypto.compute_apphmac(enc_key_0, sign_key_0).hex() で計算可能."""
|
||||
|
||||
device_key_data: bytes
|
||||
"""デバイス固有鍵データ (~6,576 bytes).
|
||||
@@ -114,7 +114,7 @@ class iOSAppbootParams:
|
||||
TODO: キャプチャから取得した値を使用すること."""
|
||||
|
||||
# オプション
|
||||
appkeyversion: int = 1
|
||||
appkeyversion: int = IOS_APPKEYVERSION
|
||||
esn_prefix: str = ""
|
||||
scheme_suffix: str = "_3"
|
||||
|
||||
@@ -158,7 +158,7 @@ class iOSMslClient:
|
||||
sign_key_0=bytes.fromhex("91f752f7..."),
|
||||
appid="a2becfec-b286-535c-b884-903a384caee6",
|
||||
devicetoken="0608a1b7...", # TODO: Frida キャプチャ
|
||||
apphmac="a18bf28f...", # TODO: Frida キャプチャ
|
||||
apphmac=NetflixCrypto.compute_apphmac(enc_key_0, sign_key_0).hex(),
|
||||
device_key_data=b"...", # TODO: Frida キャプチャ (~6576B)
|
||||
session_region=b"...", # TODO: TFIT エミュレーション or Frida
|
||||
s1=b"...", # TODO: Frida キャプチャ
|
||||
|
||||
@@ -59,6 +59,14 @@ EXPECTED_SIGN_KEY_1 = bytes.fromhex(
|
||||
"d45443fa11efec622c83b27c55f7a73143bdfa0d51820ac597b9e3fb5c28dbb0"
|
||||
)
|
||||
|
||||
# apphmac expected output
|
||||
# compute_apphmac(enc_key_0, sign_key_0):
|
||||
# prk = HMAC-SHA256(key=MGK, data=PSK)
|
||||
# okm = HMAC-SHA256(key=prk, data=Nonce)
|
||||
EXPECTED_APPHMAC = bytes.fromhex(
|
||||
"4c142e4b82b3ad21e2dcdbcc007c27a4787adc0568959080b004b5daa5a7385a"
|
||||
)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Test runner
|
||||
@@ -114,9 +122,7 @@ def main() -> int:
|
||||
session_check = hmac_mod.new(
|
||||
IOS_KDF_PSK, enc_key_0 + sign_key_0, hashlib.sha256
|
||||
).digest()
|
||||
session_bind = hmac_mod.new(
|
||||
session_check, IOS_KDF_NONCE, hashlib.sha256
|
||||
).digest()
|
||||
session_bind = hmac_mod.new(session_check, IOS_KDF_NONCE, hashlib.sha256).digest()
|
||||
check(
|
||||
"session_bind[:16] (KDF intermediate)",
|
||||
session_bind[:16],
|
||||
@@ -142,6 +148,13 @@ def main() -> int:
|
||||
check("new enc_key (16B)", new_enc_key, EXPECTED_NEW_ENC_KEY)
|
||||
check("bootstrap_key = new sign_key (32B)", new_sign_key, EXPECTED_BOOTSTRAP_KEY)
|
||||
|
||||
# ==== apphmac: HKDF(key=MGK, ikm=PSK, info=Nonce) ====
|
||||
print()
|
||||
print("=== apphmac: HKDF(MGK, PSK, Nonce) ===")
|
||||
|
||||
apphmac = NetflixCrypto.compute_apphmac(enc_key_0, sign_key_0)
|
||||
check("apphmac (32B)", apphmac, EXPECTED_APPHMAC)
|
||||
|
||||
# ==== Summary ====
|
||||
print()
|
||||
print(f"Result: {passed}/{total} passed")
|
||||
|
||||
Reference in New Issue
Block a user