Files
pywv/README.md
T

341 lines
5.7 KiB
Markdown
Raw Normal View History

2026-04-24 06:01:21 +02:00
# pywv
2026-04-24 06:06:50 +02:00
2026-04-30 05:53:23 +02:00
**pywv** is a Python-based tool for parsing, analyzing, and interacting with **Widevine (WVD) device files** and DRM structures.
It provides direct access to device data, cryptographic components, and license workflows for controlled inspection and processing.
2026-04-24 06:06:50 +02:00
---
## Features
2026-04-30 05:53:23 +02:00
* Parsing and loading of **Widevine Device (WVD) files**
* Support for **WVD version migration (v1 → v2)**
* Device loading from:
* `.wvd` files
* key + client_id blobs
* device directories
* Extraction of **device attributes** (system ID, security level)
* Decoding of **Widevine protobuf structures**
* Integration with **cryptographic primitives** (RSA, AES, CMAC, HMAC)
* Parsing and manipulation of **Widevine PSSH**
* License processing and **content key extraction**
* Optional **VMP (Verified Media Path) support**
* Built-in CDM logic (no external binaries required)
2026-04-24 06:06:50 +02:00
---
## Requirements
* Python 3.9 or higher
* `pycryptodome`
* `construct`
* `protobuf`
* `pymp4`
* `requests`
Installation:
```bash
pip install pycryptodome construct protobuf pymp4 requests
```
---
## Usage
```bash
2026-04-30 05:53:23 +02:00
python pywv.py <command> [options]
2026-04-24 06:06:50 +02:00
```
2026-04-30 05:53:23 +02:00
---
## Commands
### info
Inspect a device:
```bash
python pywv.py info --wvd device.wvd
```
Alternative input modes:
```bash
python pywv.py info --device-dir path/
python pywv.py info --key device_private_key --client-id device_client_id_blob
```
Optional:
```bash
--vmp device_vmp_blob
```
2026-04-24 06:06:50 +02:00
---
2026-04-30 05:53:23 +02:00
### license
Execute the full license workflow (challenge generation, request submission, response parsing, key extraction):
```bash
python pywv.py license \
--wvd device.wvd \
--pssh BASE64_PSSH \
--url LICENSE_URL
```
Using raw blobs:
```bash
python pywv.py license \
--key device_private_key \
--client-id device_client_id_blob \
--pssh BASE64_PSSH \
--url LICENSE_URL
```
Challenge-only mode:
```bash
python pywv.py license \
--wvd device.wvd \
--pssh BASE64_PSSH
```
---
### create-wvd
Create a WVD file from raw device components:
```bash
python pywv.py create-wvd \
--key device_private_key \
--client-id device_client_id_blob \
-o device.wvd
```
With VMP:
```bash
--vmp device_vmp_blob
```
---
### export-wvd
Export a WVD file into raw device components:
```bash
python pywv.py export-wvd \
--wvd device.wvd \
-o output_dir
```
---
### migrate-wvd
Migrate a WVD v1 file to WVD v2:
```bash
python pywv.py migrate-wvd \
-i old.wvd \
-o new.wvd
```
---
### pssh
Inspect PSSH data:
```bash
python pywv.py pssh --pssh BASE64_PSSH
```
Rewrite key IDs:
```bash
python pywv.py pssh --pssh BASE64_PSSH --set-kid NEW_KID
```
---
## Python Usage
2026-04-24 06:06:50 +02:00
### Basic Usage
```python
import requests
from pywv import PSSH, Device, Cdm
pssh = PSSH("AAAAW3Bzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAADsIARIQ62dqu8s0Xpa7z2FmMPGj2hoNd2lkZXZpbmVfdGVzdCIQZmtqM2xqYVNkZmFsa3IzaioCSEQyAA==")
2026-04-30 05:53:23 +02:00
device = Device.load("device.wvd")
2026-04-24 06:06:50 +02:00
cdm = Cdm.from_device(device)
session_id = cdm.open()
try:
challenge = cdm.get_license_challenge(session_id, pssh)
2026-04-30 05:53:23 +02:00
response = requests.post(
2026-04-24 06:06:50 +02:00
"https://cwip-shaka-proxy.appspot.com/no_auth",
data=challenge,
)
2026-04-30 05:53:23 +02:00
response.raise_for_status()
2026-04-24 06:06:50 +02:00
2026-04-30 05:53:23 +02:00
cdm.parse_license(session_id, response.content)
2026-04-24 06:06:50 +02:00
for key in cdm.get_keys(session_id):
print(f"[{key.type}] {key.kid.hex}:{key.key.hex()}")
finally:
cdm.close(session_id)
```
---
2026-04-30 05:53:23 +02:00
### Using Key / Certificate (no JSON)
2026-04-24 06:06:50 +02:00
```python
import requests
from pathlib import Path
from pywv import PSSH, Device, Cdm
2026-04-30 05:53:23 +02:00
base = Path(__file__).resolve().parent
2026-04-24 06:06:50 +02:00
pssh = PSSH("AAAAW3Bzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAADsIARIQ62dqu8s0Xpa7z2FmMPGj2hoNd2lkZXZpbmVfdGVzdCIQZmtqM2xqYVNkZmFsa3IzaioCSEQyAA==")
2026-04-30 05:53:23 +02:00
device = Device.from_files(
certificate=base / "device_client_id_blob",
key=base / "device_private_key",
vmp=False
)
2026-04-24 06:06:50 +02:00
cdm = Cdm.from_device(device)
session_id = cdm.open()
try:
challenge = cdm.get_license_challenge(session_id, pssh)
2026-04-30 05:53:23 +02:00
response = requests.post(
2026-04-24 06:06:50 +02:00
"https://cwip-shaka-proxy.appspot.com/no_auth",
data=challenge,
)
2026-04-30 05:53:23 +02:00
response.raise_for_status()
2026-04-24 06:06:50 +02:00
2026-04-30 05:53:23 +02:00
cdm.parse_license(session_id, response.content)
2026-04-24 06:06:50 +02:00
for key in cdm.get_keys(session_id):
print(f"[{key.type}] {key.kid.hex}:{key.key.hex()}")
finally:
cdm.close(session_id)
```
2026-04-30 05:53:23 +02:00
### Create WVD
```python
from pywv import Device
device = Device.from_files(
certificate="device_client_id_blob",
key="device_private_key",
vmp=False
)
device.dump("device.wvd")
```
---
### Export WVD
```python
from pathlib import Path
from pywv import Device
out = Path("exported")
out.mkdir(exist_ok=True)
device = Device.load("device.wvd")
(out / "device_private_key").write_bytes(
device.private_key.export_key("DER")
)
(out / "device_client_id_blob").write_bytes(
device.client_id.SerializeToString()
)
if device.client_id.vmp_data:
(out / "device_vmp_blob").write_bytes(
device.client_id.vmp_data
)
```
---
### Migrate WVD v1 → v2
```python
from pywv import Device
with open("old.wvd", "rb") as f:
data = f.read()
device = Device.migrate(data)
device.dump("new.wvd")
```
---
### PSSH
```python
from pywv import PSSH
pssh = PSSH("BASE64")
print(pssh.key_ids)
new_pssh = PSSH.new(
system_id=PSSH.SystemId.Widevine,
key_ids=pssh.key_ids,
version=1
)
print(new_pssh.dumps())
```
---
## Output
```
[CONTENT] kid:key
```
2026-04-24 06:06:50 +02:00
---
## Disclaimer
This tool is intended strictly for **educational, research, and interoperability purposes**.
2026-04-30 05:53:23 +02:00
The author does not support or encourage misuse of DRM systems or unauthorized access to protected content.
2026-04-24 06:06:50 +02:00
---
## Issues and Support
For bug reports, feature requests, or general inquiries, please open an issue in the repository.
---
## Acknowledgements
2026-04-30 05:53:23 +02:00
This project is based on **pywidevine**, adapted for standalone usage and improved WVD and PSSH workflows.