mirror of
https://github.com/unshackle-dl/unshackle.git
synced 2025-10-23 15:11:08 +00:00
feat(cdm): Enhance DecryptLabsRemoteCDM to support cached keys and improve license handling
This commit is contained in:
@@ -26,7 +26,9 @@ class DecryptLabsRemoteCDM(RemoteCdm):
|
|||||||
self.api_session_ids = {}
|
self.api_session_ids = {}
|
||||||
self.license_request = None
|
self.license_request = None
|
||||||
self.service_name = service_name
|
self.service_name = service_name
|
||||||
|
self.device_name = device_name
|
||||||
self.keys = {}
|
self.keys = {}
|
||||||
|
self.scheme = "L1" if device_name == "L1" else "widevine"
|
||||||
try:
|
try:
|
||||||
super().__init__(device_type, system_id, security_level, host, secret, device_name)
|
super().__init__(device_type, system_id, security_level, host, secret, device_name)
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -66,16 +68,49 @@ class DecryptLabsRemoteCDM(RemoteCdm):
|
|||||||
) -> bytes:
|
) -> bytes:
|
||||||
self.pssh = pssh
|
self.pssh = pssh
|
||||||
|
|
||||||
|
request_data = {
|
||||||
|
"init_data": self.pssh.dumps(),
|
||||||
|
"service_certificate": self.req_session.signed_device_certificate,
|
||||||
|
"scheme": self.scheme,
|
||||||
|
"service": self.service_name,
|
||||||
|
}
|
||||||
|
# Add required parameter for L1 scheme
|
||||||
|
if self.scheme == "L1":
|
||||||
|
request_data["get_cached_keys_if_exists"] = True
|
||||||
res = self.session(
|
res = self.session(
|
||||||
self.host + "/get-request",
|
self.host + "/get-request",
|
||||||
{
|
request_data,
|
||||||
"init_data": self.pssh.dumps(),
|
|
||||||
"service_certificate": self.req_session.signed_device_certificate,
|
|
||||||
"scheme": "widevine",
|
|
||||||
"service": self.service_name,
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Check if we got cached keys instead of a challenge
|
||||||
|
if res.get("message_type") == "cached-keys":
|
||||||
|
# Store cached keys directly
|
||||||
|
if session_id not in self.keys:
|
||||||
|
self.keys[session_id] = []
|
||||||
|
session_keys = self.keys[session_id]
|
||||||
|
|
||||||
|
for cached_key in res.get("cached_keys", []):
|
||||||
|
# Handle KID format - could be hex string or UUID string
|
||||||
|
kid_str = cached_key["kid"]
|
||||||
|
try:
|
||||||
|
# Try as UUID string first
|
||||||
|
kid_uuid = UUID(kid_str)
|
||||||
|
except ValueError:
|
||||||
|
try:
|
||||||
|
# Try as hex string (like the existing code)
|
||||||
|
kid_uuid = UUID(bytes=bytes.fromhex(kid_str))
|
||||||
|
except ValueError:
|
||||||
|
# Fallback: use Key.kid_to_uuid
|
||||||
|
kid_uuid = Key.kid_to_uuid(kid_str)
|
||||||
|
|
||||||
|
session_keys.append(Key(kid=kid_uuid, type_="CONTENT", key=bytes.fromhex(cached_key["key"])))
|
||||||
|
|
||||||
|
# Return empty challenge since we already have the keys
|
||||||
|
self.license_request = ""
|
||||||
|
self.api_session_ids[session_id] = None
|
||||||
|
return b""
|
||||||
|
|
||||||
|
# Normal challenge response
|
||||||
self.license_request = res["challenge"]
|
self.license_request = res["challenge"]
|
||||||
self.api_session_ids[session_id] = res["session_id"]
|
self.api_session_ids[session_id] = res["session_id"]
|
||||||
|
|
||||||
@@ -87,6 +122,10 @@ class DecryptLabsRemoteCDM(RemoteCdm):
|
|||||||
self.keys[session_id] = []
|
self.keys[session_id] = []
|
||||||
session_keys = self.keys[session_id]
|
session_keys = self.keys[session_id]
|
||||||
|
|
||||||
|
# If we already have cached keys and no session_id_api, skip processing
|
||||||
|
if session_id_api is None and session_keys:
|
||||||
|
return
|
||||||
|
|
||||||
if isinstance(license_message, dict) and "keys" in license_message:
|
if isinstance(license_message, dict) and "keys" in license_message:
|
||||||
session_keys.extend(
|
session_keys.extend(
|
||||||
[
|
[
|
||||||
@@ -96,14 +135,21 @@ class DecryptLabsRemoteCDM(RemoteCdm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
# Ensure license_message is base64 encoded
|
||||||
|
if isinstance(license_message, bytes):
|
||||||
|
license_response_b64 = base64.b64encode(license_message).decode()
|
||||||
|
elif isinstance(license_message, str):
|
||||||
|
license_response_b64 = license_message
|
||||||
|
else:
|
||||||
|
license_response_b64 = str(license_message)
|
||||||
res = self.session(
|
res = self.session(
|
||||||
self.host + "/decrypt-response",
|
self.host + "/decrypt-response",
|
||||||
{
|
{
|
||||||
"session_id": session_id_api,
|
"session_id": session_id_api,
|
||||||
"init_data": self.pssh.dumps(),
|
"init_data": self.pssh.dumps(),
|
||||||
"license_request": self.license_request,
|
"license_request": self.license_request,
|
||||||
"license_response": license_message,
|
"license_response": license_response_b64,
|
||||||
"scheme": "widevine",
|
"scheme": self.scheme,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user