mirror of
https://github.com/qtmleap/revkit.git
synced 2026-09-29 14:31:51 +02:00
- 旧ドキュメント (docs/netflix/, docs/instructions/ 等) を削除 - docs/spec/: appboot ピンニング解析、iOS再生フロー、 MSL CBOR鍵交換解析、キャプチャ復号状況、暗号化フロー図を追加 - docs/netflix_client_spec.md, platform_flow_comparison.md を追加 - iOS認証・マニフェスト・ライセンス取得フロー詳細を追加 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
7.3 KiB
7.3 KiB
Netflix MSL クライアント共通仕様
プラットフォーム共通の MSL プロトコル、マニフェスト構造、セグメントダウンロード、復号の仕様。 プラットフォーム固有の認証・エンドポイント・DRM は各プラットフォーム別仕様を参照。
1. MSL メッセージ暗号化
1.1 エンベロープ構造
MSL レスポンスは複数の JSON オブジェクトが連結された形式:
{headerdata, signature, mastertoken}{payload, signature}{payload, signature}...
- 1つ目: ヘッダー (mastertoken 含む)
- 2つ目以降: ペイロードチャンク
1.2 ペイロード暗号化フォーマット
各ペイロードチャンクの payload を base64 デコードすると:
{
"ciphertext": "<base64(AES-CBC encrypted data)>",
"sha256": "<base64(HMAC-SHA256)>",
"keyid": "{ESN}_{sequence}",
"iv": "<base64(16-byte IV)>"
}
1.3 復号手順
ivを base64 デコード (16バイト IV)ciphertextを base64 デコード (暗号文)- MSL セッションの
encryption_key(AES-128) と IV で AES-CBC 復号 - PKCS7 パディング除去
- 結果は JSON:
{data, messageid, compressionalgo, sequencenumber} dataを base64 デコード → 圧縮されたペイロード本体- 圧縮展開 (gzip または LZW) → JSON
1.4 圧縮方式
compressionalgo |
方式 | 使用プラットフォーム |
|---|---|---|
(gzip magic 1f8b) |
gzip | StreamFab, Android |
LZW |
LZW | Chrome |
2. マニフェストレスポンス構造
2.1 トップレベル
{
"id": 1775356074,
"version": 2,
"serverTime": 1775356074742,
"result": {
"movieId": 81756595,
"duration": 8523000,
"drmType": "widevine",
"drmVersion": 25,
"drmContextId": "2596051",
"playbackContextId": "E3-Bgilt-vcAxKKB...",
"expiration": 1775399301816,
"video_tracks": [...],
"audio_tracks": [...],
"timedtexttracks": [...],
"servers": [...],
"links": {
"license": { "href": "/license?licenseType=standard&playbackContextId=...&esn=...&drmContextId=..." },
"ldl": { "href": "/license?licenseType=limited&playbackContextId=...&esn=...&drmContextId=..." },
"events": { "href": "/events?playbackContextId=...&esn=..." }
}
},
"common": { "cadToken": "C1-Bgjtt-vcAxKMAQX..." },
"from": "playapi"
}
links.license.href はライセンスチャレンジ時にそのまま使用する相対パス。
2.2 ビデオトラック
{
"trackType": "PRIMARY",
"maxWidth": 1920,
"maxHeight": 1080,
"drmHeader": {
"bytes": "AAAANHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABQIARIQAAAAAAloudsAAAAAAAAAAA==",
"keyId": "AAAAAAloudsAAAAAAAAAAA==",
"drmHeaderId": "000000000968b9db0000000000000000",
"resolution": { "height": 540, "width": 960 }
},
"streams": [...]
}
drmHeader.bytes: PSSH ボックス (base64)。Widevine CDM のgenerateRequestに渡すinitDatadrmHeaderは track レベルに 1 つだが、KID 境界は stream レベル (SD 鍵 / HD 鍵)
2.3 ビデオストリーム
{
"content_profile": "av1-main-L40-dash-cbcs-prk",
"bitrate": 1412,
"peakBitrate": 9675,
"res_w": 1920,
"res_h": 1080,
"framerate_value": 24000,
"framerate_scale": 1001,
"size": 1511911636,
"startByteOffset": 67372,
"vmaf": 97,
"drmHeaderId": "000000000968b9de0000000000000000",
"downloadable_id": "1500103607",
"isDrm": true,
"moov": { "offset": 116, "size": 992 },
"sidx": { "offset": 1108, "size": 33140 },
"ssix": { "offset": 34248, "size": 33124 },
"urls": [
{ "url": "https://{cdn_host}/?o=1&v=21&e={expiration}&t={token}", "cdn_id": 140752 }
]
}
| フィールド | 説明 |
|---|---|
content_profile |
コーデック + 暗号化スキーム識別子 |
drmHeaderId |
この stream の DRM Key ID (hex 32文字) |
downloadable_id |
Netflix 内部ストリーム ID |
urls[].url |
CDN ダウンロード URL (署名付き、有効期限あり) |
moov/sidx/ssix |
MP4 ボックスオフセット (セグメントインデックスに必要) |
startByteOffset |
メディアデータ開始位置 |
size |
全セグメント合計サイズ (bytes) |
2.4 DRM スキーム対応表
| content_profile パターン | 暗号化スキーム | 暗号化方式 | 使用コーデック |
|---|---|---|---|
*-playready-* |
cenc |
AES-128-CTR (全サンプル) | H.264 |
*-cenc-* |
cenc |
AES-128-CTR (全サンプル) | HEVC |
*-cbcs-* |
cbcs |
AES-128-CBC (サブサンプル 1/10) | AV1 |
コーデックごとに異なる KID ペアが割り当てられる。 コーデック切り替え時はライセンス再取得が必要。
2.5 KID 境界 (実測)
同一コーデック内で 2 つの KID が使い分けられる:
- SD (540p 以下): SD 用 Key ID
- HD (720p 以上): HD 用 Key ID
2.6 オーディオトラック
| ソース | プロファイル | チャンネル | ビットレート |
|---|---|---|---|
| licensedmanifest | ddplus-5.1-dash, ddplus-5.1hq-dash |
5.1 | 192, 256, 384, 448, 640 kbps |
| manifest API | xheaac-dash |
2.0 | 32, 64, 96, 192 kbps |
licensedmanifest は 19 言語で 5.1ch を提供。manifest API は 2.0ch のみ。
2.7 CDN サーバー
{
"dns": { "host": "...", "ipv6": "...", "ipv4": "..." },
"id": 140752,
"rank": 1,
"type": "OPEN_CONNECT_APPLIANCE",
"name": "c014.tyo013.ix.nflxvideo.net"
}
rank が低いほど優先。urls[].cdn_id と servers[].id で対応付ける。
3. セグメントダウンロード
3.1 CDN URL 構造
https://{cdn_host}/?o=1&v={version}&e={expiration_unix}&t={signature_token}
| パラメータ | 説明 |
|---|---|
o |
常に 1 |
v |
CDN バージョン (21, 22 等) |
e |
URL 有効期限 (Unix timestamp) |
t |
署名トークン (base64url) |
3.2 DASH セグメントダウンロード手順
Range: bytes=0-{moov.offset + moov.size - 1}で初期化セグメントを取得sidxをパースしてセグメント一覧 (バイトレンジ + 時間) を構築- 各セグメントを
Rangeリクエストで取得
4. 復号
4.1 復号フロー
encrypted_segment + content_key(KID) → decrypted_segment
- セグメントの
moofボックスからtenc(Track Encryption Box) を読み取り senc(Sample Encryption Box) から各サンプルの IV を取得- Key ID に対応するコンテンツ鍵で AES-CTR (cenc) または AES-CBC (cbcs) 復号
4.2 Widevine ライセンスフロー (共通)
- マニフェストの
drmHeader.bytes(PSSH, base64) をデコード - Widevine CDM
generateRequest("cenc", psshBytes)→license-request(protobuf, ~4000 bytes) - MSL 経由で
/pbo_licenses/routerに POST - レスポンスから
license-response(protobuf, ~1000 bytes) 取得 - CDM
update(licenseResponse)でコンテンツ鍵インストール
5. 有効期限
| 項目 | フィールド | 典型値 |
|---|---|---|
| マニフェスト | expiration |
~8 時間 (manifestExpirationDuration) |
| CDN URL | e パラメータ |
~12 時間 (urlExpirationDuration) |
| Cookie | セッション依存 | 数日〜数週間 |