diff --git a/README.md b/README.md index 05f9cd1..d445c93 100644 --- a/README.md +++ b/README.md @@ -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** 1–3. 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 +# 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. \ No newline at end of file +## 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.