import base64, json, requests from enum import Enum, IntEnum from hashlib import sha1 from Cryptodome.Cipher import AES from Cryptodome.Util import Padding class TrackType(str, Enum): SD = "SD" HD = "HD" AUDIO = "AUDIO" class WidevineError(IntEnum): DRM_DEVICE_CERTIFICATE_REVOKED = 127 DRM_DEVICE_CERT_SERIAL_REVOKED = 175 INVALID_PSSH = 152 INVALID_LICENSE_CHALLENGE = 106 # ------------------------------------------------------------------- # Constants # ------------------------------------------------------------------- AES_KEY = bytes.fromhex( "1ae8ccd0e7985cc0b6203a55855a1034afc252980e970ca90e5202689f947ab9" ) AES_IV = bytes.fromhex( "d58ce954203b7c9a9a9d467f59839249" ) PROVIDER = "widevine_test" CONTENT_ID = "ZmtqM2xqYVNkZmFsa3Izag==" # example content id def build_request(challenge) -> dict: """Build signed Widevine license request payload.""" if not isinstance(challenge, str): challenge = base64.b64encode(challenge).decode() payload = json.dumps( { "payload": challenge, "provider": PROVIDER, "content_id": CONTENT_ID, "content_key_specs": [{"track_type": t.value} for t in TrackType], }, separators=(",", ":"), ).encode() request_b64 = base64.b64encode(payload).decode() # Widevine-style signature digest = sha1(base64.b64decode(request_b64)).digest() cipher = AES.new(AES_KEY, AES.MODE_CBC, AES_IV) signature = base64.b64encode(cipher.encrypt(Padding.pad(digest, 16))).decode() return { "request": request_b64, "signature": signature, "signer": PROVIDER, } def check_response(resp: dict): """Check for error status and raise if needed.""" code = resp.get("internal_status") if not code: return try: error = WidevineError(code) raise RuntimeError(f"License error: {error.name} ({code})") except ValueError: # Unknown code, ignore return if __name__ == "__main__": pssh = "CAESEFoHzpkm/EolkmbSIjR2qgkaCHVzcC1jZW5jIhhXZ2ZPbVNiOFNpV1NadElpTkhhcUNRPT0qADIA" challenge = "CAQ=" # 1. Request service certificate cert_resp = requests.post( "https://license.widevine.com/cenc/getlicense/widevine_test", json=build_request(challenge), ).json() service_cert = cert_resp.get("license") challenge = "" # use pywidevine to generate B64 challenge using pssh, service_cert # 2. Issue real license request license_resp = requests.post( "https://license.widevine.com/cenc/getlicense/widevine_test", json=build_request(challenge), ).json() check_response(license_resp) print("level:", license_resp.get("security_level")) print("systemID:", license_resp.get("system_id")) print("status:", license_resp.get("device_state"))