104 lines
4.8 KiB
Python
Raw Permalink Normal View History

2025-03-18 00:17:27 +05:30
from uuid import UUID
from Cryptodome.Random import get_random_bytes, random
from vinetrimmer.utils.widevine.device import LocalDevice
from vinetrimmer.utils.widevine.session import Session
import requests
import json
import base64
class Cdm:
#system_id = b"\xed\xef\x8b\xa9\x79\xd6\x4a\xce\xa3\xc8\x27\xdc\xd5\x1d\x21\xed"
system_id = b"\x9a\x04\xf0\x79\x98\x40\x42\x86\xab\x92\xe6\x5b\xe0\x88\x5f\x95"
uuid = UUID(bytes=system_id)
urn = f"urn:uuid:{uuid}"
service_certificate_challenge = b"\x08\x04"
common_privacy_cert = ("CAUSxwUKwQIIAxIQFwW5F8wSBIaLBjM6L3cqjBiCtIKSBSKOAjCCAQoCggEBAJntWzsyfateJO/DtiqVtZhSCtW8y"
"zdQPgZFuBTYdrjfQFEEQa2M462xG7iMTnJaXkqeB5UpHVhYQCOn4a8OOKkSeTkwCGELbxWMh4x+Ib/7/up34QGeHl"
"eB6KRfRiY9FOYOgFioYHrc4E+shFexN6jWfM3rM3BdmDoh+07svUoQykdJDKR+ql1DghjduvHK3jOS8T1v+2RC/TH"
"hv0CwxgTRxLpMlSCkv5fuvWCSmvzu9Vu69WTi0Ods18Vcc6CCuZYSC4NZ7c4kcHCCaA1vZ8bYLErF8xNEkKdO7Dev"
"Sy8BDFnoKEPiWC8La59dsPxebt9k+9MItHEbzxJQAZyfWgkCAwEAAToUbGljZW5zZS53aWRldmluZS5jb20SgAOuN"
"HMUtag1KX8nE4j7e7jLUnfSSYI83dHaMLkzOVEes8y96gS5RLknwSE0bv296snUE5F+bsF2oQQ4RgpQO8GVK5uk5M"
"4PxL/CCpgIqq9L/NGcHc/N9XTMrCjRtBBBbPneiAQwHL2zNMr80NQJeEI6ZC5UYT3wr8+WykqSSdhV5Cs6cD7xdn9"
"qm9Nta/gr52u/DLpP3lnSq8x2/rZCR7hcQx+8pSJmthn8NpeVQ/ypy727+voOGlXnVaPHvOZV+WRvWCq5z3CqCLl5"
"+Gf2Ogsrf9s2LFvE7NVV2FvKqcWTw4PIV9Sdqrd+QLeFHd/SSZiAjjWyWOddeOrAyhb3BHMEwg2T7eTo/xxvF+YkP"
"j89qPwXCYcOxF+6gjomPwzvofcJOxkJkoMmMzcFBDopvab5tDQsyN9UPLGhGC98X/8z8QSQ+spbJTYLdgFenFoGq4"
"7gLwDS6NWYYQSqzE3Udf2W7pzk4ybyG4PHBYV3s4cyzdq8amvtE/sNSdOKReuHpfQ=")
def __init__(self, device):
"""Create a Widevine Content Decryption Module using a specific devices data."""
self.sessions = {}
self.device = device
def open(self, pssh, raw=False, offline=False):
"""
Open a CDM session with the specified pssh box.
Multiple sessions can be active at the same time.
Parameters:
pssh: PSSH Data, either a full WidevineCencHeader or a full mp4 pssh box.
raw: If the PSSH Data is incomplete, e.g. NF Key Exchange, set this to True.
offline: 'OFFLINE' License Type field value.
Returns:
New Session ID.
"""
session_id = self.create_session_id(self.device)
self.sessions[session_id] = Session(session_id, pssh, raw, offline)
return session_id
def close(self, session_id):
"""
Close a CDM session.
:param session_id: Session to close.
:returns: True if Successful.
"""
if self.is_session_open(session_id):
self.sessions.pop(session_id)
return True
return False
def is_session_open(self, session_id):
return session_id in self.sessions
def set_service_certificate(self, session_id, certificate):
if not self.is_session_open(session_id):
raise ValueError(f"There's no session with the id [{session_id!r}]...")
return self.device.set_service_certificate(self.sessions[session_id], certificate)
def get_license_challenge(self, session_id):
if not self.is_session_open(session_id):
raise ValueError(f"There's no session with the id [{session_id!r}]...")
return self.device.get_license_challenge(self.sessions[session_id])
def parse_license(self, session_id, license_res):
if not self.is_session_open(session_id):
raise ValueError(f"There's no session with the id [{session_id!r}]...")
return self.device.parse_license(self.sessions[session_id], license_res)
def get_keys(self, session_id, content_only=False):
if not self.is_session_open(session_id):
raise ValueError(f"There's no session with the id [{session_id!r}]...")
keys = self.sessions[session_id].keys
if content_only:
return [x for x in keys if x.type == "CONTENT"]
return keys
@staticmethod
def create_session_id(device):
if device.type == LocalDevice.Types.ANDROID:
session_id = "{hex:16X}{counter}".format(
hex=random.getrandbits(64),
counter="01" # counter, this resets regularly so it's fine to use 01
)
session_id.ljust(32, "0") # pad to 16 bytes (32 chars)
return session_id.encode("ascii")
if device.type == LocalDevice.Types.CHROME:
return get_random_bytes(16)
if device.type == LocalDevice.Types.PLAYREADY:
return get_random_bytes(16)
raise ValueError(f"Device Type {device.type.name} is not implemented")