Update README.md

This commit is contained in:
Ap0dex
2026-05-30 11:03:25 +08:00
committed by GitHub
parent d2e209184c
commit 2a4acbf6b4
+176 -74
View File
@@ -1,15 +1,16 @@
# Netflix MSL Universal Handshake
> A cross-platform MSL (Message Security Layer) authentication toolkit for Netflix.
> Implements a unified handshake layer that works across Android, iOS, Smart TV, Web, and MGK — all driven from a single entry point with shared credential management.
> A cross-platform MSL (Message Security Layer) authentication toolkit for Netflix. Implements a unified handshake layer that works across Android, iOS, Smart TV, Web, and MGK all driven from a single entry point with shared credential management.
---
## Table of Contents
- [Requirements](#requirements)
- [Configuration](#configuration)
- [Devices (WVD Files)](#devices-wvd-files)
- [Project Structure](#project-structure)
- [Platforms](#platforms)
- [Android RSA](#android-rsa-no-wvd)
- [Android](#android)
- [iOS](#ios)
- [TV (email/password)](#tv-emailpassword)
@@ -17,18 +18,26 @@
- [Web](#web)
- [MGK (Model Group Key)](#mgk-model-group-key)
- [Usage](#usage)
- [Flags](#flags)
- [WVD Glob Testing](#wvd-glob-testing)
- [Proxy Support](#proxy-support)
- [Output Files](#output-files)
---
Install all dependencies:
## Requirements
```bash
pip install -r requirements.txt
```
Key dependencies: `pywidevine`, `pycryptodome`, `requests`, `coloredlogs`, `certifi`, `jsonpickle`.
---
## Configuration
Edit `config.ini` before running anything:
Edit `config.ini` before running:
```ini
[NETFLIX]
@@ -36,36 +45,72 @@ EMAIL = your@email.com
PASSWORD = yourpassword
```
Credentials are read once at startup and passed internally to every platform function. They are **never** accepted as command-line arguments.
Credentials are read once at startup. They are **never** accepted as command-line arguments.
---
## Devices (WVD Files)
## Project Structure
Two Widevine Device (`.wvd`) files are included in the `devices/` folder:
| File | Security Level | Used by |
|------|---------------|---------|
| `l3.wvd` | L3 | Android, iOS |
| `l1.wvd` | L1 | TV, TV OTP |
The correct WVD is selected automatically for each platform. The `--wvd` flag lets you override with a custom device file if needed.
```
NF-MSL/
├── main.py # CLI entry point
├── config.ini # Netflix credentials
├── devices/ # WVD device files (gitignored)
├── output/ # Per-platform output (gitignored)
└── modules/
├── config.py # Config loader
├── logging.py # Coloredlogs setup
├── session.py # requests.Session factory (TLS + proxy)
├── helpers.py # Shared utilities
├── msl/ # MSL protocol layer
│ ├── base.py # MSLBase, MSLKeys, send_message
│ ├── android.py # MSL_ANDROID (Widevine + RSA)
│ ├── ios.py # MSL_IOS
│ ├── tv.py # MSL_TV
│ ├── web.py # MSL_WEB (RSA)
│ └── mgk.py # MSL_MGK (AUTHENTICATED_DH)
└── platforms/ # Per-platform login orchestration
├── android_rsa.py # run_android_rsa
├── android.py # run_android
├── ios.py # run_ios
├── tv.py # run_tv
├── tv_otp.py # run_tv_otp
├── web.py # run_web
└── mgk.py # run_mgk
```
---
## Platforms
### Android
### Android RSA (no WVD)
Emulates a **Samsung Galaxy Z Flip3 (SM-F711N)** running Android 15.
Emulates a **Samsung Galaxy Z Flip3 (SM-F711N)** running Android 15. Uses an RSA/ASYMMETRIC_WRAPPED key exchange — no Widevine device file required.
**MSL flow:**
1. Bootstrap HTTP session → obtain `nfvdid` cookie via `appboot`
2. Widevine key exchange (MSL handshake) using L3 WVD
3. `VerifyLoginMslRequest` — submits email + password via MSL
4. Decrypts the response header to extract `useridtoken`
1. Bootstrap HTTP session → `nfvdid` cookie via `appboot`
2. RSA key exchange (MSL handshake) using `NFCDCH-02-*` ESN
3. `CLCSScreenUpdate` GraphQL — submit email + password via MSL
4. Load `/browse` to finalize session
**Output:** `netflix_auth_tokens.json`, `netflix_auth_useridtoken.json`, `netflix_auth_cookies.json`
**Output:** `android/netflix_auth_cookies_rsa.json`, `netflix_auth_tokens_rsa.json`
---
### Android
Emulates a **Samsung Galaxy Z Flip3 (SM-F711N)** running Android 15 with full Widevine L3 authentication.
**MSL flow:**
1. Bootstrap HTTP session → `nfvdid` via `appboot`
2. Widevine key exchange using the provided `.wvd`
3. Load login page → `CLCSScreenUpdate` (CLCS web login) to obtain `NetflixId`/`SecureNetflixId` cookies
4. `VerifyLoginMslRequest` (samurai) — binds the MSL session to the authenticated account
5. Decrypt response header → extract `useridtoken`
> If `VerifyLoginMslRequest` returns `incorrect_password`, the CLCS login fallback is triggered automatically and the request is retried with the fresh auth cookies.
**Output (per WVD system ID):** `android/netflix_auth_tokens_{sid}.json`, `netflix_auth_useridtoken_{sid}.json`, `netflix_auth_cookies_{sid}.json`
---
@@ -74,13 +119,13 @@ Emulates a **Samsung Galaxy Z Flip3 (SM-F711N)** running Android 15.
Emulates an **iPhone 15 Pro Max** running iOS 18.
**MSL flow:**
1. Bootstrap HTTP session → obtain `nfvdid` via `appboot`
2. Widevine key exchange using L3 WVD
1. Bootstrap HTTP session → `nfvdid` via `appboot`
2. Widevine key exchange using the provided `.wvd`
3. `MembershipStatus` GraphQL probe (anonymous)
4. `CLCSScreenUpdate` — submits email + password via MSL to `ios.prod.cloud.netflix.com/graphql`
5. Decrypts the response header to extract `useridtoken`
4. Load login page → extract `clcsSessionId` + `renditionId`
5. `CLCSScreenUpdate` — submit email + password via MSL to `ios.prod.cloud.netflix.com/graphql`
**Output:** `netflix_auth_cookies.json`
**Output (per WVD system ID):** `ios/netflix_auth_cookies_{sid}.json`
---
@@ -91,15 +136,15 @@ Emulates an **NVIDIA SHIELD Android TV (2019)**.
**MSL flow:**
1. Obtain `nfvdid` from the Android TV config endpoint
2. Bootstrap AUI + pre-login `pathEvaluator`
3. Widevine key exchange using L1 WVD`mintCookies`
3. Widevine key exchange using the provided `.wvd``mintCookies`
4. CLCS session initiation (`clcsLegacyMoneyballInitiateSession`)
5. Multi-step sign-in flow:
- Navigate welcome landing → web sign-in → email → password path
5. Multi-step credential flow:
- Welcome landing → web sign-in → email → password
- Submit credentials via `clcsScreenUpdate`
6. Post-login PBO config + token refresh (`getPartnerToken`, `ping`)
7. Save cookies including `NetflixId`, `SecureNetflixId`, `gsid`
**Output:** `netflix_cookies.json`, `useridtoken.json`, `msl_debug_trace.json`, `password_login_response.json`
**Output (per WVD system ID):** `tv/netflix_cookies_{sid}.json`, `useridtoken_{sid}.json`, `msl_debug_trace_{sid}.json`, `password_login_response_{sid}.json`
---
@@ -111,12 +156,12 @@ Same device profile as **TV**, but authenticates via a **one-time pairing code**
13. Same as TV up through `mintCookies`
4. CLCS session initiation
5. Navigate to `webSignIn` mode → extract an **8-digit TV code**
6. **Display the code and poll** `https://www.netflix.com/tv2` until the user activates it from a browser
6. **Display the code and poll** `https://www.netflix.com/tv2` every 5 seconds until the user activates it
7. Send `continueAction` to complete sign-in
> **Interactive:** you must visit `https://www.netflix.com/tv2` in a browser and enter the displayed code to proceed.
> **Interactive:** visit `https://www.netflix.com/tv2` in a browser and enter the displayed code to proceed.
**Output:** `netflix_cookies.json`, `useridtoken.json`
**Output (per WVD system ID):** `tv_otp/netflix_cookies_{sid}.json`, `useridtoken_{sid}.json`
---
@@ -125,16 +170,16 @@ Same device profile as **TV**, but authenticates via a **one-time pairing code**
Emulates **Chrome 146 on Windows 10**.
**MSL flow:**
1. Bootstrap anonymous browser session (`netflix.com``netflix.com/login`)
1. Bootstrap anonymous browser session (`netflix.com``/login`)
2. `MembershipStatus` GraphQL probe
3. Extract `clcsSessionId` and `renditionId` from the login page HTML
4. `CLCSScreenUpdate` — submit email + password directly to `PASSWORD_LOGIN` screen
5. Optional `CLCSSendFeedback` if the response contains a feedback payload
6. Open `/browse` to finalize the authenticated session
3. Extract `clcsSessionId` + `renditionId` from login page HTML
4. `CLCSScreenUpdate` — submit email + password to `PASSWORD_LOGIN`
5. Optional `CLCSSendFeedback` if the server returns a feedback payload
6. Load `/browse` to finalize session
7. `CLCSInterstitialProfileGate` probe
8. ALE provision via MSL (`aleProvision` handshake)
8. ALE provision via MSL RSA handshake
**Output:** `netflix_auth_cookies.json`
**Output:** `browser/netflix_auth_cookies.json`
---
@@ -149,78 +194,135 @@ Uses the **MGK / AUTHENTICATED_DH** MSL key-exchange scheme, authenticating with
| `KpeKph` | Base64 AES-128 encryption key + Base64 HMAC-SHA256 key, comma-separated |
| `ESNID` | Model-group identity string (the MGK sender ESN) |
These files are discovered automatically in the working directory, any subdirectory, or via environment variables:
Files are auto-discovered in the working directory, any subdirectory, or via environment variables:
```bash
export MSL_KPEKPH_PATH=/path/to/KpeKph
export MSL_ESNID_PATH=/path/to/ESNID
```
Or pass `--kpekph` on the command line.
Or pass `--kpekph` and `--esnid` on the command line.
**MSL flow:**
1. Load `KpeKph` → derive wrapping key
2. Generate a DH keypair; build `AUTHENTICATED_DH` key-request with `mechanism=MGK`
3. Perform handshake → derive session encryption + HMAC keys from shared secret
2. Generate DH keypair build `AUTHENTICATED_DH` key-request (`mechanism=MGK`)
3. Handshake → derive session encryption + HMAC keys from shared secret
4. Send `EMAIL_PASSWORD` user-auth message → receive `useridtoken`
**Output:** `useridtoken_mgk.json`, `netflix_auth_cookies_mgk.json`
**Output:** `mgk/netflix_auth_cookies_mgk.json`
---
## Usage
```bash
# Android
python main.py --platform android
# Android RSA (no WVD needed)
python main.py --platform android_rsa
# Android with a single WVD
python main.py --platform android --wvd devices/my_device_l3.wvd
# iOS
python main.py --platform ios
python main.py --platform ios --wvd devices/my_device_l3.wvd
# TV (email/password)
python main.py --platform tv
python main.py --platform tv --wvd devices/my_device.wvd
# TV OTP (pairing code — interactive)
python main.py --platform tv_otp
python main.py --platform tv_otp --wvd devices/my_device.wvd
# Web (Chrome emulation)
python main.py --platform web
# MGK (Model Group Key)
python main.py --platform mgk
# MGK
python main.py --platform mgk --esnid path/to/ESNID --kpekph path/to/KpeKph
# MGK with explicit KpeKph path
python main.py --platform mgk --kpekph /path/to/KpeKph
# Force fresh MSL handshake (ignore cached keys)
python main.py --platform tv --wvd devices/my_device.wvd --new-msl
# Override WVD device for Android/iOS/TV/TV-OTP
python main.py --platform android --wvd /path/to/device.wvd
# Force a fresh MSL key exchange (ignore cached keys)
python main.py --platform tv --new-msl
# Disable TLS verification (not recommended)
# Skip TLS verification
python main.py --platform web --no-verify
# Pass a reCAPTCHA token for web login
python main.py --platform web --recaptcha-token <token>
# Use a proxy
python main.py --platform tv --wvd devices/my_device.wvd --proxy http://127.0.0.1:8080
python main.py --platform tv --wvd devices/my_device.wvd --proxy http://user:pass@proxy.example.com:3128
```
### All flags
---
## Flags
| Flag | Applies to | Description |
|------|-----------|-------------|
| `--platform` / `-p` | all | **Required.** `android`, `ios`, `tv`, `tv_otp`, `web`, `mgk` |
| `--wvd` | android, ios, tv, tv_otp | Path to `.wvd` Widevine device file (optional override) |
| `--kpekph` | mgk | Path to `KpeKph` key file |
| `--new-msl` | all | Force a fresh MSL handshake, ignoring any cached keys |
| `--platform` | all | **Required.** One of `android_rsa`, `android`, `ios`, `tv`, `tv_otp`, `web`, `mgk` |
| `--wvd` | android, ios, tv, tv_otp | Path or glob to `.wvd` file(s). **Required** for these platforms |
| `--kpekph` | mgk | Path to `KpeKph` key file (auto-discovered if omitted) |
| `--esnid` | mgk | ESN identity string or file path. **Required** for mgk |
| `--new-msl` | all | Force a fresh MSL handshake, ignoring cached keys |
| `--no-verify` | all | Disable TLS certificate verification |
| `--recaptcha-token` | web | reCAPTCHA v2 response token |
| `--proxy` | all | Proxy URL — `http://ip:port` or `http://user:pass@ip:port` |
---
MSL key caches are reused across runs to avoid a full handshake every time. They expire automatically when the master token has fewer than 10 hours remaining.
## WVD Glob Testing
Pass a glob pattern to `--wvd` to test multiple `.wvd` files in sequence. Only `.wvd` files are matched.
```bash
python main.py --platform android --wvd "devices/*.wvd"
python main.py --platform tv --wvd "devices/*"
```
The tool loops through all matching files sorted by name, waits **10 seconds between each** to avoid throttling, and prints a pass/fail summary at the end:
```
MSL HANDSHAKE - INFO - Found 14 .wvd file(s) to test
MSL HANDSHAKE - INFO - --- [1/14] WVD: changhong_androidtv_22594_l3.wvd ---
...
MSL HANDSHAKE - INFO - === Results: 3/14 passed ===
MSL HANDSHAKE - INFO - PASS: changhong_androidtv_22594_l3.wvd
MSL HANDSHAKE - WARNING - FAIL: amlogic_mbox_22594_l3.wvd
```
Output files include the WVD's Widevine system ID, so runs never overwrite each other:
```
output/tv/netflix_cookies_22594.json
output/tv/useridtoken_22594.json
```
---
**Big thanks to [Hugoved](https://github.com/Hugoved)**
- for the foundational work on MSL (Message Security Layer) reverse engineering, and the original pywidevine implementation that made this unified handshake toolkit possible.
## Proxy Support
All platforms support an HTTP/HTTPS proxy. The proxy is applied at both the session level (all HTTP requests) and explicitly on every MSL `send_message` call.
```bash
# IP:port
python main.py --platform tv --wvd devices/device.wvd --proxy http://192.168.1.1:8080
# Authenticated
python main.py --platform tv --wvd devices/device.wvd --proxy http://user:pass@proxy.host:3128
```
---
## Output Files
All output is written under the `output/` directory, organised by platform. Files that depend on a WVD include the Widevine **system ID** in their name so multiple WVDs can be tested without overwriting results.
| Platform | Output files |
|----------|-------------|
| `android_rsa` | `android/netflix_auth_cookies_rsa.json`, `netflix_auth_tokens_rsa.json`, `netflix_auth_useridtoken_rsa.json` |
| `android` | `android/netflix_auth_cookies_{sid}.json`, `netflix_auth_tokens_{sid}.json`, `netflix_auth_useridtoken_{sid}.json` |
| `ios` | `ios/netflix_auth_cookies_{sid}.json` |
| `tv` | `tv/netflix_cookies_{sid}.json`, `useridtoken_{sid}.json`, `msl_debug_trace_{sid}.json`, `password_login_response_{sid}.json` |
| `tv_otp` | `tv_otp/netflix_cookies_{sid}.json`, `useridtoken_{sid}.json` |
| `web` | `browser/netflix_auth_cookies.json` |
| `mgk` | `mgk/netflix_auth_cookies_mgk.json` |
MSL key caches are also stored per platform (and per WVD system ID where applicable) and reused across runs. They expire automatically when the master token has fewer than **10 hours** remaining.
---
**Big thanks to [Hugoved](https://github.com/Hugoved)**
- for the foundational work on MSL (Message Security Layer) reverse engineering and the original pywidevine implementation that made this unified handshake toolkit possible.