Files
revkit/docs/spec/00_common.md
T
tkgstratorandClaude Opus 4.6 d91af8380b docs: Netflix仕様書の再構成とiOS解析ドキュメント追加
- 旧ドキュメント (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>
2026-04-07 02:06:14 +00:00

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 復号手順

  1. iv を base64 デコード (16バイト IV)
  2. ciphertext を base64 デコード (暗号文)
  3. MSL セッションの encryption_key (AES-128) と IV で AES-CBC 復号
  4. PKCS7 パディング除去
  5. 結果は JSON: {data, messageid, compressionalgo, sequencenumber}
  6. data を base64 デコード → 圧縮されたペイロード本体
  7. 圧縮展開 (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 に渡す initData
  • drmHeader は 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 セグメントダウンロード手順

  1. Range: bytes=0-{moov.offset + moov.size - 1} で初期化セグメントを取得
  2. sidx をパースしてセグメント一覧 (バイトレンジ + 時間) を構築
  3. 各セグメントを Range リクエストで取得

4. 復号

4.1 復号フロー

encrypted_segment + content_key(KID) → decrypted_segment
  1. セグメントの moof ボックスから tenc (Track Encryption Box) を読み取り
  2. senc (Sample Encryption Box) から各サンプルの IV を取得
  3. Key ID に対応するコンテンツ鍵で AES-CTR (cenc) または AES-CBC (cbcs) 復号

4.2 Widevine ライセンスフロー (共通)

  1. マニフェストの drmHeader.bytes (PSSH, base64) をデコード
  2. Widevine CDM generateRequest("cenc", psshBytes) → license-request (protobuf, ~4000 bytes)
  3. MSL 経由で /pbo_licenses/router に POST
  4. レスポンスから license-response (protobuf, ~1000 bytes) 取得
  5. CDM update(licenseResponse) でコンテンツ鍵インストール

5. 有効期限

項目 フィールド 典型値
マニフェスト expiration ~8 時間 (manifestExpirationDuration)
CDN URL e パラメータ ~12 時間 (urlExpirationDuration)
Cookie セッション依存 数日〜数週間