diff --git a/README.md b/README.md index d5e0c97..d09dc2e 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,8 @@ cdm.parse_license(session_id, response.text) for key in cdm.get_keys(session_id): print(f"{key.key_id.hex}:{key.key.hex()}") + +cdm.close(session_id) ``` ## Disclaimer diff --git a/pyplayready/cdm.py b/pyplayready/cdm.py index 2d385ba..df907e9 100644 --- a/pyplayready/cdm.py +++ b/pyplayready/cdm.py @@ -3,7 +3,7 @@ from __future__ import annotations import base64 import math import time -from typing import List +from typing import List, Union from uuid import UUID import xml.etree.ElementTree as ET @@ -32,9 +32,9 @@ class Cdm: def __init__( self, security_level: int, - certificate_chain: CertificateChain, - encryption_key: ECCKey, - signing_key: ECCKey, + certificate_chain: Union[CertificateChain, None], + encryption_key: Union[ECCKey, None], + signing_key: Union[ECCKey, None], client_version: str = "10.0.16384.10011", protocol_version: int = 1 ): diff --git a/pyplayready/main.py b/pyplayready/main.py index 83eca9c..9cfff2b 100644 --- a/pyplayready/main.py +++ b/pyplayready/main.py @@ -52,6 +52,9 @@ def license_(device_path: Path, pssh: PSSH, server: str) -> None: cdm = Cdm.from_device(device) log.info("Loaded CDM") + session_id = cdm.open() + log.info("Opened Session") + challenge = cdm.get_license_challenge(pssh.get_wrm_headers(downgrade_to_v4=True)[0]) log.info("Created License Request (Challenge)") log.debug(challenge) @@ -78,6 +81,9 @@ def license_(device_path: Path, pssh: PSSH, server: str) -> None: for key in cdm.get_keys(): log.info(f"{key.key_id.hex}:{key.key.hex()}") + cdm.close(session_id) + log.info("Clossed Session") + @main.command() @click.argument("device", type=Path) @@ -232,7 +238,7 @@ def export_device(ctx: click.Context, prd_path: Path, out_dir: Optional[Path] = @main.command("serve", short_help="Serve your local CDM and Playready Devices Remotely.") @click.argument("config_path", type=Path) @click.option("-h", "--host", type=str, default="127.0.0.1", help="Host to serve from.") -@click.option("-p", "--port", type=int, default=8786, help="Port to serve from.") +@click.option("-p", "--port", type=int, default=7723, help="Port to serve from.") def serve_(config_path: Path, host: str, port: int) -> None: """ Serve your local CDM and Playready Devices Remotely. diff --git a/pyplayready/remotecdm.py b/pyplayready/remotecdm.py index 2d778ff..317e131 100644 --- a/pyplayready/remotecdm.py +++ b/pyplayready/remotecdm.py @@ -5,9 +5,7 @@ import re import requests from pyplayready.cdm import Cdm -from pyplayready.bcert import CertificateChain from pyplayready.device import Device -from pyplayready.ecc_key import ECCKey from pyplayready.key import Key from pyplayready.exceptions import (DeviceMismatch, InvalidInitData) @@ -49,7 +47,7 @@ class RemoteCdm(Cdm): self.device_name = device_name # spoof certificate_chain and ecc_key just so we can construct via super call - super().__init__(security_level, CertificateChain, ECCKey, ECCKey) + super().__init__(security_level, None, None, None) self.__session = requests.Session() self.__session.headers.update({ @@ -97,20 +95,18 @@ class RemoteCdm(Cdm): def get_license_challenge( self, session_id: bytes, - pssh: str, - downgrade: str + wrm_header: str, ) -> str: - if not pssh: - raise InvalidInitData("A pssh must be provided.") - if not isinstance(pssh, str): - raise InvalidInitData(f"Expected pssh to be a {str}, not {pssh!r}") + if not wrm_header: + raise InvalidInitData("A wrm_header must be provided.") + if not isinstance(wrm_header, str): + raise InvalidInitData(f"Expected wrm_header to be a {str}, not {wrm_header!r}") r = self.__session.post( url=f"{self.host}/{self.device_name}/get_license_challenge", json={ "session_id": session_id.hex(), - "init_data": pssh, - "downgrade": downgrade, + "init_data": wrm_header, } ).json() if r["status"] != 200: diff --git a/pyplayready/serve.py b/pyplayready/serve.py index 60d9bda..175a91c 100644 --- a/pyplayready/serve.py +++ b/pyplayready/serve.py @@ -115,7 +115,7 @@ async def get_license_challenge(request: web.Request) -> web.Response: device_name = request.match_info["device"] body = await request.json() - for required_field in ("session_id", "init_data", "downgrade"): + for required_field in ("session_id", "init_data"): if not body.get(required_field): return web.json_response({ "status": 400, @@ -125,11 +125,6 @@ async def get_license_challenge(request: web.Request) -> web.Response: # get session id session_id = bytes.fromhex(body["session_id"]) - # get downgrade - downgrade = False - if body['downgrade'] == 'true': - downgrade = True - # get cdm cdm: Optional[Cdm] = request.app["cdms"].get((secret_key, device_name)) if not cdm: @@ -139,13 +134,14 @@ async def get_license_challenge(request: web.Request) -> web.Response: }, status=400) # get init data - init_data = PSSH(body["init_data"]).get_wrm_headers(downgrade_to_v4=downgrade) + # init_data = PSSH(body["init_data"]).get_wrm_headers(downgrade_to_v4=downgrade) + init_data = body["init_data"] # get challenge try: license_request = cdm.get_license_challenge( session_id=session_id, - content_header=init_data[0], + content_header=init_data, ) except InvalidSession: return web.json_response({