Python implementation of Google's Widevine DRM CDM (Content Decryption Module)
v1.4.0
to v1.4.2
PSSH.overwrite_key_ids()
as an instance method now named PSSH.set_key_ids()
.PSSH.get_key_ids()
as a property method named PSSH.key_ids
. This allows swift access to all the Key IDs of
the current PSSH object data.PSSH.from_playready_pssh()
as an instance method now named PSSH.playready_to_widevine()
that now converts
the current instances values directly. This allows you to more easily instance as any PSSH, then convert after wards
and only if wanted and when needed.v1.4.0
to v1.4.2
PSSH.new()
.
PSSH.dump()
.PSSH.dumps()
.Cdm.get_keys()
.
Cdm._sessions
object./get_keys
endpoint.PSSH.get_as_box()
has been merged into the PSSH constructor, simplifying usage of the PSSH class.PSSH.from_playready_pssh()
is now a class method and returns as a PSSH object.Cdm.get_license_challenge()
.
/challenge
endpoint has been changed to /get_license_challenge
, and /keys
to /parse_license
.
/parse_license
endpoint.
/get_keys
endpoint should be used to retrieve keys.Cdm._sessions
class instance variable even more to Cdm.__sessions
.
cdm._Cdm__sessions
.PSSH.from_key_ids()
has been removed entirely, you should now use PSSH.new(key_ids=...)
instead.Session
object has been removed from RemoteCdm
. The session is now fully controlled by the
remote API and de-synchronization by external alteration or unexpected exceptions is no longer a possibility.key_id
to key_ids
in the PSSH class.DecodeError
and SignatureMismatch
exceptions in the Serve /set_service_certificate
endpoint.InvalidInitData
and InvalidLicenseType
exceptions in the Serve /get_license_challenge
endpoint./parse_license
endpoint.RemoteCdm
with improved error handling.v1.3.0
to v1.3.1
device_type
parameter in Cdm
s constructor.force_privacy_mode
to be defined in the config file. It now assumes a default of false.pywidevine serve ...
instead of the full project url in the Server header.RemoteCdm
s Server version check is now case-insensitive.RemoteCdm
s Server version check now ignores other Server/Proxy names prepended or appended to the Server header.
v1.3.0
to v1.3.1
RemoteCdm
class. It has an identical interface as the original Cdm
class.
Cdm
in this update or any in the future, will also be done to RemoteCdm./set_service_certificate
as an improved way of setting (or unsetting) the service certificate.Cdm
s constructor now uses more direct values, so you don't have to use the Device class or .wvd
files.
.wvd
files you must now use Cdm.from_device()
instead.None
to `Cdm.set_service_certificate()./challenge
endpoint no longer accepts a service_certificate
item in the JSON payload.
/set_service_certificate
endpoint before calling /challenge
.SignedDrmCertificate
and SignedMessages
messages in Cdm.encrypt_client_id()
. This is mainly as a
convenience for any scripts wanting to encrypt their Client ID with a service certificate manually./keys
endpoint can now be received by providing ALL
as the key type.
CONTENT
keys if that's all you need./close
endpoint to close a session. All clients should close the session once they are finished
with it or the user will eventually hit a limit of 50 sessions per user and the server will hog memory til it
restarts.Server
header denoting that pywidevine serve is being used, and it's version.
lxml
from ^4.9.1
to >=4.8.0
to support projects using pycaption.SignedMessage
with a SignedDrmCertificate
instead of the raw
DrmCertificate
. The SignedMessage
is unsigned as the SignedDrmCertificate
within it, is signed. This is so
anything inheriting or using the Cdm (e.g., serve
) can verify the certificate down the chain and keep it signed./{device}
prefix. E.g., instead of /challenge/STREAMING
, it's now
/device_name/challenge/STREAMING
. This is to support the previous change./open
endpoint by returning a 400 error.PSSH.get_as_box()
.serve
that hosts a CDM API that can be externally accessed with authentication. This can be used to
access and/or share your CDM without exposing your Widevine device private key, or even it's identity by enforcing
Privacy Mode.
serve
extras, i.e., pip install pywidevine[serve]
.127.0.0.1
blocks access outside your network, even if port-forwarded. Use
-h 0.0.0.0
to allow remote access.serve.example.yml
in the project root folder has verbose documentation on available options.migrate
.PSSH.get_as_box()
to raise an Exception if passed data is not already a box, as it has been improved
to create a new box if not detected as a box already.Cdm.parse_license()
are now rejected if they are not of LICENSE
type.Cdm.set_service_certificate()
are now verified. This patches a trivial "exploit"
that allows an attacker to recover the plaintext Client ID from a license under Privacy Mode. See
https://gist.github.com/rlaphoenix/74acabdd7269a21845e18b621c5860ef.PSSH.get_as_box()
now supports arbitrary and box data automatically as it tries to detect if it is a
valid box, otherwise makes a new box.Cdm
constructor's parameter pssh
to init_data
, as that's what the Cdm actually wants and uses,
whereas a PSSH
is an mp4
atom (aka box) containing init_data
(a Widevine CENC Header). The full PSSH is never
kept nor ever used. It still accepts PSSH box data.Cdm.set_service_certificate()
instead of the passed
certificate, of which they would already have.Cdm.open()
.Cdm.get_license_challenge()
.Cdm.close(session_id)
.Cdm.parse_license()
and now must be obtained directly from cdm._sessions
.
for key in cdm._sessions[session_id].keys: print(f"[{key.type}] {key.kid.hex}:{key.key.hex()}")
.Cdm.decrypt()
method.pywidevine.exceptions
.Cdm.parse_license()
. Any further attempts will raise an
InvalidContext
exception.
DrmCertificate
s are no longer supported by Cdm.set_service_certificate()
as they have no signature.
See the 3rd Change above. Provide either a SignedDrmCertificate
or a SignedMessage
containing a
SignedDrmCertificate
. A SignedMessage
containing a DrmCertificate
will also be rejected.PSSH.from_init_data()
, use PSSH.get_as_box()
.raw
parameter of Cdm
constructor, as well as CLI commands as it is now handled upstream by the PSSH
creation.PSSH.get_as_box()
.Cdm.parse_license()
.type_
parameter in Cdm.get_license_challenge()
.type_
parameter if is a string in Cdm.get_license_challenge()
.create-device
to create .wvd
files (Widevine Device files) from RSA PEM/DER Private Keys and
Client ID blobs. You can also provide VMP (FileHashes) data which will be merged into the Client ID blob.migrate
that uses Device.migrate()
and dump()
to migrate older v1 Widevine Device files to v2.Device
method migrate()
to load an older Widevine Device file format. It is recommended to then use the
dumps()
method to save it as a new v2 Widevine Device file, which can then be loaded normally.SignedDrmCertificate
and DrmCertificate
messages in Cdm.set_service_certificate()
. Services can provide
the certificate as a SignedMessage
, SignedDrmCertificate
, or a DrmCertificate
. Only SignedMessage
and
SignedDrmCertificate
are signed.test
CLI command with the -p/--privacy
flag..wvd
Widevine Device file structures from Device
to a _Structures
class in device.py
. The
_Structures
class can be imported and used directly, or via Device.structures
.migrate
to Device.migrate()
. The
CLI command migrate
now internally uses Device.migrate()
.DrmCertificate
s instead of a SignedMessage
as the signature and other
data in the message is unused and unneeded.send_key_control_nonce
from v1 and v2 Structures as it was only used before initial
release, and isn't a necessary nor useful flag.type
to type_
in Device.dump()
.Cdm
constructor to it's get_license_challenge()
method.Cdm.raw
class instance variable.Cdm.set_service_certificate()
.