mirror of
https://github.com/Hugoved/pywv.git
synced 2026-07-15 18:10:06 +02:00
Update README.md
This commit is contained in:
@@ -1,2 +1,156 @@
|
||||
# pywv
|
||||
pywv is a Python tool for parsing and working with Widevine (WVD) device files, PSSH data, and DRM-related cryptographic structures.
|
||||
|
||||
**pywv** is a Python-based tool for parsing, analyzing, and interacting with **Widevine (WVD) device files** and associated DRM structures. It provides low-level access to device data, cryptographic components, and license-related metadata, enabling precise inspection and controlled handling of Widevine workflows.
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
* Parsing and loading of **Widevine Device (WVD) files**
|
||||
* Support for **WVD version migration (v1 → v2)**
|
||||
* Extraction of **device attributes** (system ID, security level, client identification)
|
||||
* Decoding of **Widevine protobuf structures** (ClientIdentification, certificates, licenses)
|
||||
* Integration with **cryptographic primitives** (RSA, AES, CMAC, HMAC)
|
||||
* Parsing and manipulation of **PSSH (Widevine / PlayReady)** containers
|
||||
* License processing and **content key extraction** via CDM workflows
|
||||
|
||||
---
|
||||
|
||||
## Requirements
|
||||
|
||||
* Python 3.9 or higher
|
||||
* `pycryptodome`
|
||||
* `construct`
|
||||
* `protobuf`
|
||||
* `pymp4`
|
||||
* `requests`
|
||||
|
||||
Installation:
|
||||
|
||||
```bash
|
||||
pip install pycryptodome construct protobuf pymp4 requests
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
python pywv.py [options]
|
||||
```
|
||||
---
|
||||
|
||||
## Examples
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```python
|
||||
import requests
|
||||
from pywv import PSSH, Device, Cdm
|
||||
|
||||
pssh = PSSH("AAAAW3Bzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAADsIARIQ62dqu8s0Xpa7z2FmMPGj2hoNd2lkZXZpbmVfdGVzdCIQZmtqM2xqYVNkZmFsa3IzaioCSEQyAA==")
|
||||
|
||||
device = Device.load("xiaomi_redmi_note_4x_15.0.0_e15a9e5f_8159_l3.wvd")
|
||||
cdm = Cdm.from_device(device)
|
||||
session_id = cdm.open()
|
||||
|
||||
try:
|
||||
challenge = cdm.get_license_challenge(session_id, pssh)
|
||||
|
||||
license_response = requests.post(
|
||||
"https://cwip-shaka-proxy.appspot.com/no_auth",
|
||||
data=challenge,
|
||||
)
|
||||
license_response.raise_for_status()
|
||||
|
||||
cdm.parse_license(session_id, license_response.content)
|
||||
|
||||
for key in cdm.get_keys(session_id):
|
||||
print(f"[{key.type}] {key.kid.hex}:{key.key.hex()}")
|
||||
|
||||
finally:
|
||||
cdm.close(session_id)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Using JSON Device Configuration
|
||||
|
||||
```python
|
||||
# wv.json example:
|
||||
# {
|
||||
# "security_level": 3,
|
||||
# "session_id_type": "android",
|
||||
# "private_key_available": true,
|
||||
# "vmp": false
|
||||
# }
|
||||
|
||||
import json
|
||||
import requests
|
||||
from pathlib import Path
|
||||
from pywv import PSSH, Device, Cdm
|
||||
|
||||
base_path = Path(__file__).resolve().parent
|
||||
|
||||
with open(base_path / "wv.json", "r", encoding="utf-8") as f:
|
||||
wv = json.load(f)
|
||||
|
||||
pssh = PSSH("AAAAW3Bzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAADsIARIQ62dqu8s0Xpa7z2FmMPGj2hoNd2lkZXZpbmVfdGVzdCIQZmtqM2xqYVNkZmFsa3IzaioCSEQyAA==")
|
||||
|
||||
if not wv.get("private_key_available", True):
|
||||
raise ValueError("This device profile does not include a private key")
|
||||
|
||||
device_kwargs = {
|
||||
"private_key": base_path / "device_private_key",
|
||||
"client_id": base_path / "device_client_id_blob",
|
||||
"type_": wv.get("session_id_type", "ANDROID").upper(),
|
||||
"security_level": wv.get("security_level", 3),
|
||||
}
|
||||
|
||||
vmp_path = base_path / "device_vmp_blob"
|
||||
if wv.get("vmp", False) and vmp_path.exists():
|
||||
device_kwargs["vmp"] = vmp_path
|
||||
|
||||
device = Device.from_files(**device_kwargs)
|
||||
|
||||
cdm = Cdm.from_device(device)
|
||||
session_id = cdm.open()
|
||||
|
||||
try:
|
||||
challenge = cdm.get_license_challenge(session_id, pssh)
|
||||
|
||||
license_response = requests.post(
|
||||
"https://cwip-shaka-proxy.appspot.com/no_auth",
|
||||
data=challenge,
|
||||
)
|
||||
license_response.raise_for_status()
|
||||
|
||||
cdm.parse_license(session_id, license_response.content)
|
||||
|
||||
for key in cdm.get_keys(session_id):
|
||||
print(f"[{key.type}] {key.kid.hex}:{key.key.hex()}")
|
||||
|
||||
finally:
|
||||
cdm.close(session_id)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This tool is intended strictly for **educational, research, and interoperability purposes**.
|
||||
The author does not support or encourage the misuse of DRM systems or unauthorized access to protected content.
|
||||
|
||||
---
|
||||
|
||||
## Issues and Support
|
||||
|
||||
For bug reports, feature requests, or general inquiries, please open an issue in the repository.
|
||||
|
||||
Support and updates will be provided as availability permits.
|
||||
|
||||
---
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
This project is **based on pywidevine**, with modifications and restructuring to support standalone usage and extended functionality for working with WVD and PSSH data.
|
||||
|
||||
Reference in New Issue
Block a user