Files
revkit/docs/plans/ios_msl_client_library.md
T
tkgstrator d23836e618 feat(docs,tools): add iOS MSL analysis docs and HKDF param search scripts
Introduces detailed documentation for the iOS Netflix MSL client, including work plans and a comprehensive decryption pipeline, to support reverse engineering and protocol understanding.

Adds Python scripts that automate brute-force searching of HKDF parameters, support key derivation analysis across multiple encryption and HMAC key candidates, and verify DH parameter consistency.

Facilitates future development of a standalone iOS MSL client and aids in identifying the correct key derivation logic needed for cross-platform manifest decryption.
2026-04-08 10:12:57 +00:00

134 lines
7.3 KiB
Markdown

# 作業計画書: iOS MSL クライアントライブラリ
日時: 2026-04-08T19:10:00+09:00
## 目標
Netflix iOS MSL 通信を Python で独立実装し、動画マニフェスト取得まで実行する。
## 判明済みの事実
### 暗号パラメータ (全て確定済み)
| パラメータ | 値 | 備考 |
|-----------|-----|------|
| DH 素数 p | `9694e9d8...e27df` (1024-bit) | NFWebCrypto にハードコード、セッション間で固定 |
| DH 生成元 g | `5` | 固定 |
| AES モード | AES-128-CBC + PKCS7 | enc_key = 16 bytes |
| HMAC | HMAC-SHA256 | hmac_key = 32 bytes |
| IV 格納 | ciphertext の先頭 16 bytes に prepend | IV フィールド (key 7) は空 |
### CBOR メッセージ構造 (全て確定済み)
| メッセージ種別 | トップレベルキー | 特徴 |
|--------------|----------------|------|
| appboot リクエスト | {16, 33, 34} + {16, 64} | entity_auth_data (34) あり、header (32) なし |
| appboot レスポンス | {16, 32, 33} + {16, 64} | key_response_data (33) にセッション鍵 |
| 通常リクエスト (manifest 等) | {16, 32, 33} + {16, 64} | entity_auth なし、header (32) あり |
| 通常レスポンス | 同上 | gzip ラップされる場合あり |
### entity_auth_data 内部構造 (FAIRPLAY_MGK_APPID)
```
key 30: "FAIRPLAY_MGK_APPID"
key 35: {
50: bytes(8096) ← device_key_data (デバイス固有)
80: "NFAPPL-02-IPHONE9=1-" ← ESN プレフィックス
81: "NFAPPL-02-IPHONE9=1-AD04..." ← PRV ESN 全体
"apphmac": bytes(32) ← HMAC-SHA256
"appid": "a2becfec-b286-535c-b884-903a384caee6"
"appkeyversion": 1
"devicetoken": bytes(216)
}
```
### key_exchange_data 構造
```
appboot: key 6 = bytes(352), key 8 = "{ESN}" (suffix なし)
通常 MSL: key 6 = bytes(1216), key 8 = "{ESN}_5" (suffix あり)
共通: key 7 = bytes(0), key 9 = bytes(16) nonce
```
### HTTP ヘッダー (キャプチャ確認済み)
```
Host: appboot.netflix.com
Content-Type: application/x-www-form-urlencoded
User-Agent: Netflix/24 CFNetwork/1335.0.3.4 Darwin/21.6.0
X-Netflix.APIAction: appboot
X-Netflix.Request.Attempt: 1
Cookie: nfvdid=...
```
## 未解明の要素
| 項目 | 重要度 | 解明方法 |
|------|--------|----------|
| **HKDF パラメータ** (shared_secret → enc_key + hmac_key) | ★★★ 必須 | Tweak で HKDF 関数をフック、または総当たり (salt/info/length の組み合わせ) |
| **entity_auth_data の生成ロジック** | ★★☆ | キャプチャからバイナリテンプレートとして再利用可能。独自生成は TFIT 解明が必要で困難 |
| **key 33.6 の構造** (352B/1216B) | ★★☆ | DH pub_key (128B) + 追加データの関係を解明。キャプチャテンプレート再利用も可 |
| **HMAC 署名対象** | ★☆☆ | キャプチャの signature と hmac_key で検証対象データを特定 |
## タスク一覧
### Phase 0: 残る未解明要素の調査 (並列)
#### tweak-engineer 担当
- [ ] T-1. **HKDF パラメータのキャプチャ** — NFWebCrypto の HKDF シンボルを特定してフック。salt, info, output length を記録
- [ ] T-2. **ESN キャプチャ** — `IosMslClient.esn` getter をフックして `g_keys[@"esn"]` に保存
- [ ] T-3. **Cookie キャプチャ** — `NSHTTPCookieStorage.cookiesForURL:` をフックし NetflixId / SecureNetflixId を抽出
#### python-engineer 担当
- [ ] P-0. **HKDF 総当たり検証** — キャプチャ済みの shared_secret / enc_key / hmac_key を使い、一般的な HKDF パラメータ (SHA-256, 空 salt, 各種 info 文字列) で enc_key が再現できるか検証。Tweak の HKDF フックと並行して進める
- [ ] P-1. **HMAC 署名対象の特定** — キャプチャの signature (key 16) と hmac_key を照合し、署名入力データの範囲を確定
- [ ] P-2. **キャプチャテンプレート抽出** — appboot / manifest リクエストの entity_auth_data, key_exchange_data, header をバイナリテンプレートとして `raws/templates/` に保存するスクリプト作成
### Phase 1: エンコーダー・クライアント実装
#### python-engineer 担当
- [ ] P-3. **DH 鍵交換の Python 実装** — `crypto.py` に `generate_dh_keypair(p, g)`, `compute_dh_shared_secret(priv_key, server_pub, p)`, `derive_session_keys(shared_secret)` を追加
- [ ] P-4. **iOS バイナリフレームのエンコード** — `cbor_encoder.py` に `build_ios_payload_frame()` を追加。復号で判明した構造 (bstr(9) + 0x3E + gzip bstr + trailer) を再現
- [ ] P-5. **encrypt_payload の IV prepend 対応** — ciphertext の先頭に IV を prepend し、IV フィールドを空にする iOS 形式
- [ ] P-6. **`ios_client.py` 新規作成** — Encoder + Decoder + HTTP を統合した iOS MSL クライアント
- `load_session(keys_path)` — 鍵ロード (Tweak JSON 対応)
- `load_templates(template_dir)` — entity_auth / header テンプレートをロード
- `set_cookies(nfvdid, netflix_id, secure_netflix_id)` — Cookie 設定
- `appboot()` — DH 鍵交換 + セッション確立
- `get_manifest(video_id)` — マニフェスト取得
### Phase 2: マニフェスト取得
#### python-engineer 担当
- [ ] P-7. **manifest リクエスト構築** — 復号済みキャプチャから manifest ペイロード JSON の構造を再現
- [ ] P-8. **`tools/fetch_manifest.py` CLI 作成** — `--keys`, `--cookies`, `--video-id` で実行
- [ ] P-9. **差分検証** — 生成した CBOR と実キャプチャの構造比較ツール
## 実行順序
```
Phase 0 (並列):
├── [tweak] T-1 (HKDF), T-2 (ESN), T-3 (Cookie)
└── [python] P-0 (HKDF検証), P-1 (HMAC), P-2 (テンプレート)
↓
Phase 1 (順次):
P-3 (DH実装) → P-4 (フレームエンコード) → P-5 (IV prepend) → P-6 (クライアント)
↓
Phase 2 (順次):
P-7 (manifest構築) → P-8 (CLI) → P-9 (検証)
```
## 成果物
| ファイル | 説明 |
|---------|------|
| `src/netflix_msl/ios_client.py` | iOS MSL クライアント (メイン) |
| `src/netflix_msl/crypto.py` | DH 鍵交換 + HKDF 追加 (既存拡張) |
| `src/netflix_msl/cbor_encoder.py` | iOS フレームエンコード追加 (既存拡張) |
| `src/netflix_msl/constants.py` | iOS 固有定数追加 (既存拡張) |
| `tools/extract_msl_template.py` | テンプレート抽出 CLI |
| `tools/fetch_manifest.py` | マニフェスト取得 CLI |
| `raws/templates/` | 抽出済みテンプレート |
| `packages/tweak/AppbootKeyExtract/` | HKDF / ESN / Cookie キャプチャ拡張 |
## リスク・注意点
- **HKDF が最大のブロッカー**: shared_secret → enc_key + hmac_key の導出方法が判明しないと、Python 独立での鍵交換ができない。Tweak キャプチャ鍵で代替可能だが、毎回デバイスが必要になる
- **entity_auth_data はテンプレート再利用**: TFIT ホワイトボックス鍵導出の解明は現実的でないため、キャプチャした entity_auth_data をそのまま再利用する。デバイス/アカウント固有のため、別デバイスでは使えない
- **key 33.6 (352B/1216B)** の内部構造: DH pub_key (128B) + 認証データの関係が未解明。テンプレート内の pub_key 部分のみ差し替えが必要
- **Cookie の有効期限**: 定期的に再取得が必要
- **レート制限**: Netflix サーバーへのリクエスト頻度に注意