devine-dl / pywidevine

Python implementation of Google's Widevine DRM CDM (Content Decryption Module)
GNU General Public License v3.0
537 stars 118 forks source link

500 Server Error when using RemoteCDM on BitMovin demo. #40

Closed c36554 closed 1 year ago

c36554 commented 1 year ago

Describe the bug 500 Server Error when using RemoteCDM on BitMovin demo.

To Reproduce Steps to reproduce the behavior:

  1. Call set_service_certificate(session_id, Cdm.common_privacy_cert)
  2. Licence set successfully.
  3. get_license_challenge successful.
  4. Posting the challenge to the license URL gives error 500. INVALID CHALLENGE

Expected behavior Challenge is valid.

Additional context Works fine without privacy mode, same provision on same machine. Testing using local WVD through the CLI works as well.

Relevant code. Redacted for privacy

pssh = PSSH("AAAAW3Bzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAADsIARIQ62dqu8s0Xpa7z2FmMPGj2hoNd2lkZXZpbmVfdGVzdCIQZmtqM2xqYVNkZmFsa3IzaioCSEQyAA==")
#license URL
license_url = "https://cwip-shaka-proxy.appspot.com/no_auth"

cdm = RemoteCdm("ANDROID", 1234, 3, "server_url", "secret_key", "test_device_01")

# open cdm session
session_id = cdm.open()

cdm.set_service_certificate(session_id, Cdm.common_privacy_cert)

# get license challenge
challenge = cdm.get_license_challenge(session_id, pssh, "STREAMING", True)

# send license challenge (assuming a generic license server SDK with no API front)
license = requests.post(license_url, data=challenge)
license.raise_for_status()
rlaphoenix commented 1 year ago

Is this on v1.6.0 or latest git code? And to be clear, locally using Cdm directly with the same provision and privacy mode, works OK, but not with RemoteCdm?

c36554 commented 1 year ago

Strange. Installed from pip like a day ago, on multiple machines. But pywidevine --version says 1.5.3, but pip install says 1.6.0.

So I assume the version number from pywidevine is wrong? Otherwise, yes. Locally same provision works in privacy mode. Tested with pywidevine test -p test_device_01.wvd and without privacy flag, both working.

rlaphoenix commented 1 year ago

pywidevine --version says 1.5.3, but pip install says 1.6.0.

Correct, this is a bug with the 1.6.0 release. You do have 1.6.0 installed.

Since you confirmed it's working locally in all forms, it's likely a bug with the RemoteCdm class passing a value incorrectly. I'll take a look into it very soon.

rlaphoenix commented 1 year ago

I'm unable to reproduce this error on the latest code at least. I've noticed in your reproduction steps you listed:

  1. Call set_service_certificate(session_id, Cdm.common_privacy_cert)

You used the common_privacy_cert which is for license.google.com provider ID, which is not what the license server for the Bitmovin Demo uses (it uses https://cwip-shaka-proxy.appspot.com/no_auth which is a gateway to staging.google.com).

The following basic example remotely licenses the Bitmovin Art of Motion Demo with Privacy Mode by asking for the current service certificate using a special static challenge. This on my end returns a service cert for the staging.google.com provider ID, which in the latest code is statically accessible with Cdm.staging_privacy_cert.

import requests

from pywidevine import RemoteCdm
from pywidevine.device import DeviceTypes
from pywidevine.pssh import PSSH

# prepare pssh
pssh = PSSH("AAAAW3Bzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAADsIARIQ62dqu8s0Xpa"
            "7z2FmMPGj2hoNd2lkZXZpbmVfdGVzdCIQZmtqM2xqYVNkZmFsa3IzaioCSEQyAA==")

# load cdm
cdm = RemoteCdm(
    device_type=DeviceTypes.ANDROID,
    system_id=4445,
    security_level=3,
    host="http://localhost:3001",
    secret="redacted",
    device_name="redacted"
)

# open cdm session
session_id = cdm.open()

# get service certificate
service_certificate_res = requests.post(
    url="https://cwip-shaka-proxy.appspot.com/no_auth",
    data=cdm.service_certificate_challenge
)
service_certificate_res.raise_for_status()
service_certificate = service_certificate_res.content

# set service certificate
cdm.set_service_certificate(session_id, service_certificate)

# get license challenge
challenge = cdm.get_license_challenge(session_id, pssh)

# send license challenge (assuming a generic license server SDK with no API front)
licence = requests.post("https://cwip-shaka-proxy.appspot.com/no_auth", data=challenge)
licence.raise_for_status()

# parse license challenge
cdm.parse_license(session_id, licence.content)

# print keys
for key in cdm.get_keys(session_id):
    print(f"[{key.type}] {key.kid.hex}:{key.key.hex()}")

# close session, disposes of session data
cdm.close(session_id)

On my end it is returning a list of content keys and the signing key correctly without a problem.

c36554 commented 1 year ago

Confirmed working on my end. Wasn't aware it was using staging, apologies. Can close this issue, unless you want to keep it open for version number.