Improve old Changelog entries

This commit is contained in:
rlaphoenix 2023-11-21 09:56:12 +00:00
parent 9479c069b5
commit 00532979b6
1 changed files with 264 additions and 207 deletions

View File

@ -7,46 +7,47 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [1.6.0] - 2023-02-03 ## [1.6.0] - 2023-02-03
- Supported Serve API: `v1.4.3` or newer
### Added ### Added
- Added full support for Python 3.11. - Support Python 3.11.
- Added new `export-device` command-line function to export WVD files back as files. I.e., a private key and client ID - New CLI command `export-device` to export WVD files back as files. I.e., a private key and client ID blob file.
blob file.
### Changed
- The PyYAML dependency is now required even if you do not install Pywidevine with the `serve` extra dependencies.
- This was required for exporting WVD metadata in the new `export-device` command-line function.
## [1.5.3] - 2022-12-27 ## [1.5.3] - 2022-12-27
- Supported Serve API: `v1.4.3` or newer
### Added ### Added
- Added a new utility `load_xml()` to parse XML data with lxml ignoring Namespaces. - New utility `load_xml()` to parse XML data with lxml ignoring Namespaces.
- PSSH class now has a `__str__` and `__repr__` representation to print the object in more Human-friendly and - PSSH class now have `__str__` and `__repr__` methods to print the object in more Human-friendly ways.
useful ways. `str(pssh)` is now identical to `pssh.dumps()` and `repr(pssh)` or just `pssh` in some cases will - `str(pssh)` is now identical to `pssh.dumps()`.
result in a nice overview of the PSSHs contents. - `repr(pssh)` or just `pssh` in some cases will result in a nice overview of the PSSHs contents.
- Added new `to_playready()` method to convert Widevine PSSH Data to PlayReady PSSH Data. Please note that the - New `to_playready()` method to convert Widevine PSSH Data to PlayReady PSSH Data. Please note that the
Checksums for AES-CTR and COCKTAIL KIDs cannot be calculated as the Content Encryption Key would be needed. Checksums for AES-CTR and COCKTAIL KIDs cannot be calculated as the Content Encryption Key would be needed.
### Changed ### Changed
- You must now explicitly specify the System ID to use when creating a new PSSH box. - The System ID must now be explicitly specified when creating a new PSSH box in `PSSH.new()`.
This allows you to now create PlayReady PSSH boxes. - This allows you to now create PlayReady PSSH boxes.
- The `playready_to_widevine()` method has been renamed to just `to_widevine()`. - The `playready_to_widevine()` method has been renamed to just `to_widevine()`.
### Fixed ### Fixed
- Fix the capitalization of the `key_IDs` field, and it's value when creating a new PSSH box. - Correct capitalization of the `key_IDs` field when making the new box in `PSSH.new()`.
- Fix the ability to create v0 PSSH boxes by only setting the `key_IDs` field when the version is set to `1`. - Correct the value type of `key_IDs` value when creating a new box in `PSSH.new()`.
- Fix parsing of Key IDs within PlayReadyHeaders by using the new `load_xml()` utility to ignore namespaces so - Ensure Key IDs are list of UUIDs instead of bytes in `PSSH.new()`.
that `xpath` can correctly locate any and all KID tags. - Create v0 PSSH boxes by only setting the `key_IDs` field when the version is set to `1` in `PSSH.new()`.
- Fix loading of PlayReadyHeaders (and PlayReadyObjects) as PSSH boxes. It would previously load it under the - Fix loading of PlayReadyHeaders (and PlayReadyObjects) as PSSH boxes. It would previously load it under the
Widevine SystemID breaking all PlayReady-specific code after construction. Widevine SystemID breaking all PlayReady-specific code after construction.
- Fix support for loading PlayReadyObjects with more than one PlayReadyHeader (more than one record). - Parse Key IDs within PlayReadyHeaders by using the new `load_xml()` utility to ignore namespaces so that `xpath` can
correctly locate any and all KID tags.
- Support parsing PlayReadyObjects with more than one PlayReadyHeader (more than one record).
## [1.5.2] - 2022-10-11 ## [1.5.2] - 2022-10-11
- Supported Serve API: `v1.4.3` or newer
### Fixed ### Fixed
- Fixed license signature calculation for newer Widevine Server licenses on OEM Crypto v16.0.0 or newer. - Fixed license signature calculation for newer Widevine Server licenses on OEM Crypto v16.0.0 or newer.
@ -54,324 +55,380 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [1.5.1] - 2022-10-23 ## [1.5.1] - 2022-10-23
- Supported Serve API: `v1.4.3` or newer
### Added ### Added
- Added import path shortcuts in the `__init__.py` package constructor to all the user classes. Now you can do e.g., - Support for big-int Key IDs in `PSSH`. All integer values are converted to a UUID and are loaded big-endian.
`from pywidevine import PSSH` instead of `from pywidevine.pssh import PSSH`. You can still do it both ways. - Import path shortcuts in the `__init__.py` package constructor to all the user classes.
- Improved error handling and sanitization checks when parsing some Service Certificates in `set_service_certificate()`. - Now you can do e.g., `from pywidevine import PSSH` instead of `from pywidevine.pssh import PSSH`.
- You can still do it the full direct way if you want.
- Parsing check to the raw DrmCertificate in `Cdm.set_service_certificate()`.
### Changed ### Changed
- Maximum concurrent Cdm sessions are now set to 16 as it seems tto be a more common limit on more up-to-date CDMs, - Service Certificates are now stored in the session as a `SignedDrmCertificate`.
including Android's OEMCrypto Library. This also helps encourage people to close their sessions when they are no - This is to keep the signature with the Certificate, without wrapping it in a SignedMessage unnecessarily.
longer required. - Reduced the maximum concurrent Cdm sessions from 50 to 16 as it seems to be a more common limit on more up-to-date
- Service Certificates are now stored in the session as a `SignedDrmCertificate`. This is to keep the signature with devices and versions of OEMCrypto. This also helps encourage people to close their sessions when they are no longer
the stored Certificate for use by the user if necessary. It also reduces code repetition relating to the usage of the required.
signature.
### Fixed ### Fixed
- Improved reliability of computing License Signatures. Some license messages when parsed would be slightly different - Acquisition of the Certificate's provider_id in `Cdm.set_service_certificate()` in some edge cases, but also when you
when re-serialized with `SerializeToString()`, therefore the computed signature would have always mismatched. try to remove the certificate by setting it to `None`.
- Added support for Key IDs that are integer values. Effectively all values are now considered to be a UUID as 16 bytes - When exporting a PSSH object it will now do so in the same version it was initially loaded or created in. Previously
(in hex or bytes) or an integer value with support for up to 16 bytes. All integer values are converted to a UUID and it would always dump as a v1 PSSH box due to a cascading check in pymp4. It now also honors the currently set version
are loaded big-endian. in the case it gets overridden.
- Fixed acquisition of the Certificate's provider_id within `set_service_certificate()` in some edge cases, but also - Improved reliability of computing License Signatures by verifying the signature against the original raw License
when you try to remove the certificate by setting it to `None`. message instead of the re-serialized version of the message.
- PSSH now dumps in the same version the PSSH was loaded or created in. Previously it would always dump as a v1 PSSH - Some license messages when parsed would be slightly different when re-serialized against my protobuf, therefore the
box due to a cascading check in pymp4. It now also honors the currently set version in the case it gets overridden. computed signature would have always mismatched.
## [1.5.0] - 2022-09-24 ## [1.5.0] - 2022-09-24
With just one change this brings along a reduced dependency tree, smoother experience across different platforms, and - Supported Serve API: `v1.4.3` or newer
speed improvements (especially on larger input messages).
### Changed ### Changed
- Updated protobuf dependency to v4.x branch with recompiled proto-buffers. They now also have python stub files. - Updated `protobuf` dependency to `v4.x` branch with recompiled proto-buffers, specifically `v4.21.6`.
## [1.4.4] - 2022-09-24 ## [1.4.4] - 2022-09-24
- Supported Serve API: `v1.4.3` or newer
### Security ### Security
- Updated `protobuf` dependency to v3.19.5 due to the Security Advisory [GHSA-8gq9-2x98-w8hf]. - Updated `protobuf` dependency to `3.19.5` due to the Security Advisory [GHSA-8gq9-2x98-w8hf].
[GHSA-8gq9-2x98-w8hf]: <https://github.com/protocolbuffers/protobuf/security/advisories/GHSA-8gq9-2x98-w8hf> [GHSA-8gq9-2x98-w8hf]: <https://github.com/protocolbuffers/protobuf/security/advisories/GHSA-8gq9-2x98-w8hf>
## [1.4.3] - 2022-09-10 ## [1.4.3] - 2022-09-10
RemoteCdm minimum supported Serve API version is now v1.4.3. - Supported Serve API: `v1.4.3` or newer
### Added ### Added
- Cdm now has a `get_service_certificate()` endpoint to get the currently set service certificate of a Session. - Serve's `/get_license_challenge` endpoint can now disable privacy mode per-request, even if a service certificate is
RemoteCdm and Serve also has support for these endpoints. set, as long as privacy mode is not enforced in the Serve API config.
- New Cdm method `get_service_certificate()` to get the currently set service certificate of a Session.
### Changed ### Changed
- Added installation instructions, troubleshooting steps, a minimal example, and a list of features to the README. - All f-string formatting in log statements have been replaced with logging formatting to save performance when that
- The minimum version for lxml has been upped to >=4.9.1. This is due to some vulnerabilities present in all older log wouldn't have been printed.
versions. - The Serve APIs `/open` endpoint's function has been renamed from `open()` to `open_()` to prevent shadowing the
- All f-string formatting in log statements have been replaced with logging formatting to improve performance when built-in `open`.
logging is disabled.
### Security
- Updated `lxml` dependency to `>=4.9.1` due to the Security Advisory [GHSA-wrxv-2j5q-m38w].
[GHSA-wrxv-2j5q-m38w]: <https://github.com/advisories/GHSA-wrxv-2j5q-m38w>
### Removed ### Removed
- The Protocol image has been removed from the README as it is too broad to Browser scenarios and some stuff on it - The Protocol image has been removed from the README as it is too broad to Browser scenarios and some stuff on it
is too broad. If the viewer is really interested they can Google it to get a much better view into the Protocol. is too broad. If the viewer is really interested they can Google it to get a much better view into the Protocol.
### Fixed
- Serve's get_license_challenge can now disable privacy mode even if a service certificate is set, as long as privacy
mode is not enforced in settings.
## [1.4.2] - 2022-09-05 ## [1.4.2] - 2022-09-05
- Supported Serve API: `v1.4.0` to `v1.4.2`
### Changed ### Changed
- Device's constructor no longer throws `ValueError` exceptions if it fails to parse the provided Client ID or it's - Sessions in `Cdm.open()` are now initialized with a unique session number.
VMP data if any. It will now raise a `DecodeError`. - Android Cdm Devices now use a Request ID formula similar to OEMCrypto library when generating a Challenge.
This formula has yet to be fully confirmed and ironed out, but it is closer than the Chrome Cdm formula.
- `Device` no longer throws `ValueError` exceptions on `DecodeErrors` if it fails to parse the provided Client ID, or
it's VMP data if any. It will now re-raise `DecodeError`.
### Fixed ### Fixed
- Android Cdm Devices now use a Request ID formula similar to OEMCrypto library when generating a Challenge. - Parsed Proto Messages now go through an elaborate yet efficient verification, it must parse and serialize back to it's
This formula has yet to be fully confirmed and ironed out, but it is better than the Chrome Cdm formula. received form, byte-for-byte, or it will be rejected.
- Various Proto Message Parsing now has full verification and expects the parsed response to be the same length - This prevents protobuf from parsing a message that could be a different message depending on the starting bytes.
as the serialized input, or it will throw an error. For example, this prevents vague errors to happen when you - It was possible to bypass some minor checks by providing specially crafted messages that parsed as other messages.
provide a bad License to `Cdm.parse_license`. It also prevents possibilities of it going past various other checks However, I haven't noticed any way where this would lead to a vulnerability or anything bad. It mostly just lead to
depending on the first few bytes provided. Serve API crashes or just rejected messages down the chain as they wouldn't have the right data within them.
## [1.4.1] - 2022-08-17 ## [1.4.1] - 2022-08-17
Small patch release for some fixes to the PSSH classes recent face-lift. - Supported Serve API: `v1.4.0` to `v1.4.2`
### Changed ### Changed
- `PSSH.overwrite_key_ids` static method is now an instance method named `set_key_ids` and works on the current - Rework `PSSH.overwrite_key_ids()` as an instance method now named `PSSH.set_key_ids()`.
instance instead of making and returning a new one. - Rework `PSSH.get_key_ids()` as a property method named `PSSH.key_ids`. This allows swift access to all the Key IDs of
- `PSSH.get_key_ids` static method is now a property method named `key_ids`. This allows swift access to all the the current PSSH object data.
Key IDs of the current access. - Rework `PSSH.from_playready_pssh()` as an instance method now named `PSSH.playready_to_widevine()` that now converts
- `PSSH.from_playready_pssh` class method is now an instance method named `playready_to_widevine` and now converts the current instances values directly. This allows you to more easily instance as any PSSH, then convert after wards
the current instances values directly. This allows you to more easily instance as any PSSH, then convert afterwards. and only if wanted and when needed.
## [1.4.0] - 2022-08-06 ## [1.4.0] - 2022-08-06
This release is a face-lift for the PSSH class with a moderate amount of Cdm and Serve interface changes. - Supported Serve API: `v1.4.0` to `v1.4.2`
You will likely need to make a moderate amount of changes in your client code, please study the changelog.
Please note that while it was always privatized as `_sessions`, accessing the Session directly for any purpose was
never recommended or supported. With v1.4.0, there will be drastic problems if you continue to do so. One of the
few reasons to do that was to get the license keys which is no longer required with CDMs new `get_keys()` method.
RemoteCdm minimum supported Serve API version is now v1.4.0.
### Added ### Added
- The PSSH class now has a `new()` method to craft a new PSSH box. The box can be crafted from arbitrary init_data - New PSSH boxes can now be manually crafted with `PSSH.new()`.
and/or key_ids. If only key_ids is supplied a new Widevine Cenc Header will be created and the key IDs will be put - The box can be crafted from arbitrary init_data and/or key_ids.
into it. This allows you to make compliant v0 or v1 boxes with as little data as just a Key ID. - If only key_ids is supplied a new Widevine CENC Header will be created and the key IDs will be put into it.
- The PSSH class now has `dump()` and `dumps()` methods to serialize the data as binary or base64 respectively. It will - This allows you to make compliant v0 or v1 boxes with as little data as just a Key ID.
be serialized as a pymp4 PSSH box, ready to be used in an MP4 file. - PSSH boxes can now be exported as MP4 Box objects using pymp4 with `PSSH.dump()`.
- Cdm now has a method `get_keys()` to get the keys of the loaded license. This is the alternative to manually - PSSH boxes can now also be exported as Base64 strings with `PSSH.dumps()`.
accessing the keys by navigating the `_sessions` class instance variable. - License Keys can now be obtained from a Cdm session with a parsed license using `Cdm.get_keys()`.
- Serve API now also has a `/get_keys` endpoint to call the `get_keys()` method of the underlying Cdm session. - This is the alternative to manually accessing the keys from the `Cdm._sessions` object.
- It is also available on the Serve API through the new `/get_keys` endpoint.
### Changed ### Changed
- Cdm and RemoteCdm now expect a PSSH object as the `init_data` param for `get_license_challenge`. You can no longer - `PSSH.get_as_box()` has been merged into the PSSH constructor, simplifying usage of the PSSH class.
provide it anything else, that includes base64 or bytes form. It must be a PSSH object. - `PSSH.from_playready_pssh()` is now a class method and returns as a PSSH object.
- Serve no longer returns license keys in the response of the `/keys` endpoint. - Only PSSH objects are now accepted by `Cdm.get_license_challenge()`.
- Serve has changed the endpoint `/challenge` to `/get_license_challenge` and `/keys` to `/parse_license`. This is to - You can no longer provide it anything else, that includes base64 or bytes form.
be consistent with the method names of the underlying Cdm class. - You should first parse or make a new PSSH with the PSSH class, and then pass that object.
- The PSSH class has been reworked from being a static helper class to a proper PSSH class. - This is to simplify typing and repetition across the codebase.
- PSSH.from_playready_pssh is now a class method and returns as a PSSH object. - Serve's `/challenge` endpoint has been changed to `/get_license_challenge`, and `/keys` to `/parse_license`.
- This is to be consistent with the method names of the underlying Cdm class.
- Serve now passes the license type value as-is (as a string) instead of parsing it to an integer.
- Serve now passes the key type value as-is (as a string) instead of parsing it to an integer.
- Serve no longer returns license keys in the response of the `/parse_license` endpoint.
- Once parsed, the `/get_keys` endpoint should be used to retrieve keys.
- Privatized the `Cdm._sessions` class instance variable even more to `Cdm.__sessions`.
- If you still need something from it, while not advised, you can call it via `cdm._Cdm__sessions`.
### Removed ### Removed
- PSSH.get_as_box has been removed and merged into the PSSH constructor. - `PSSH.from_key_ids()` has been removed entirely, you should now use `PSSH.new(key_ids=...)` instead.
- PSSH.from_key_ids has been removed entirely, you should now use `PSSH.new(key_ids=...)` instead. - Unnecessary parsing of the license message received by RemoteCdm is now skipped. Parsing should be done by the Serve
- All uses of a local Session() object has been removed from RemoteCdm. The session is now fully controlled by the API as it will be able to actually decrypt and verify the message.
- All uses of a local `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. remote API and de-synchronization by external alteration or unexpected exceptions is no longer a possibility.
### Fixed ### Fixed
- Various uses of the `key_ids` field of WidevinePsshData proto has been fixed in the PSSH class. - Correct the WidevinePsshData proto field name from `key_id` to `key_ids` in the PSSH class.
- Fixed a few Serve API crashes in edge cases with improved error handling on Cdm method calls. - Handle `DecodeError` and `SignatureMismatch` exceptions in the Serve `/set_service_certificate` endpoint.
- Handle `InvalidInitData` and `InvalidLicenseType` exceptions in the Serve `/get_license_challenge` endpoint.
- Handle various exceptions in the Serve `/parse_license` endpoint.
- Handle various client-side runtime errors in `RemoteCdm` with improved error handling.
## [1.3.1] - 2022-08-04 ## [1.3.1] - 2022-08-04
- Supported Serve API: `v1.3.0` to `v1.3.1`
### Added ### Added
- Cdm and RemoteCdm can now be supplied a string value for `device_type` for scenarios where providing it as a string - String value support to the `device_type` parameter in `Cdm`s constructor.
is more convenient (e.g., from Config files).
### Changed
- Serve no longer requires `force_privacy_mode` to be defined in the config file. It now assumes a default of false.
- Serve now uses `pywidevine serve ...` instead of the full project url in the Server header.
- `RemoteCdm`s Server version check is now case-insensitive.
### Fixed ### Fixed
- The `force_privacy_mode` key no longer needs to be defined at all in the configuration file. This was previously - `RemoteCdm`s Server version check now ignores other Server/Proxy names prepended or appended to the Server header.
crashing serve APIs if it wasn't set before starting. - For example, if reverse-proxied through Caddy it may have prepended "Caddy" to the Server header.
- RemoteCdm's Server version check will no longer fail under certain serving conditions e.g., Caddy prepending `Caddy`
to the Server header value. It also fixes case sensitivity and removed the full url from the header.
## [1.3.0] - 2022-08-04 ## [1.3.0] - 2022-08-04
- Supported Serve API: `v1.3.0` to `v1.3.1`
### Added ### Added
- New RemoteCdm class to be used as Client code for the `serve` Remote CDM API server. The RemoteCdm should be used - New Client for using the Serve API; `RemoteCdm` class. It has an identical interface as the original `Cdm` class.
entirely separately from the normal Cdm class. All serve APIs must update to v1.3.0 to be compatible. The RemoteCdm - However, the constructor is different. Instead of passing a Widevine device object, you need to pass information
verifies the server version to ensure compatibility. Changes to the serve API schema will be immediately reflected in about the API like its host (including port if not on a reverse-proxy), and info about the device like its name and
the RemoteCdm code in the future. security level.
- Implemented `/set_service_certificate` endpoint in serve schema as an improved way of setting the service certificate - Other than that, once the RemoteCdm object is created, you use it exactly the same. Magic!
than passing it to `/challenge`. - Any time there's a change or fix to `Cdm` in this update or any in the future, will also be done to RemoteCdm.
- You can now unset the service certificate by providing an empty service certificate value (or None or null). This - New Serve endpoint `/set_service_certificate` as an improved way of setting (or unsetting) the service certificate.
includes support for doing so even in serve API and the new RemoteCdm.
### Changed ### Changed
- The Construction of the Cdm object has changed. You can now initialize it with more direct values if you don't want - `Cdm`s constructor now uses more direct values, so you don't have to use the Device class or `.wvd` files.
to use the Device class or don't want to use `.wvd` files. To use Device classes, you must now use the - To continue using `.wvd` files you must now use `Cdm.from_device()` instead.
`Cdm.from_device()` class method. - You can now unset the Service certificate by providing `None` to `Cdm.set_service_certificate().
- The ability to pass the certificate to `/challenge` has been removed. Please use the new `/set_service_certificate`
endpoint before calling `/challenge`. You do not need to set it every time. Once per session is enough unless you ### Removed
now want to use a different certificate.
- Serve's `/challenge` endpoint no longer accepts a `service_certificate` item in the JSON payload.
- Instead, use the new `/set_service_certificate` endpoint before calling `/challenge`.
- You do not need to set it every time. Once per session is enough unless you now want to use a different certificate.
## [1.2.1] - 2022-08-02 ## [1.2.1] - 2022-08-02
This release is primarily a maintenance release for `serve` functionality but some Cdm fixes are also present.
### Added ### Added
- You can now return all License Keys from Serve's `/keys` endpoint by supplying `ALL` as the key type. - Support `SignedDrmCertificate` and `SignedMessages` messages in `Cdm.encrypt_client_id()`. This is mainly as a
This adds support for Exchange Systems like Netflix's WidevineExchange MSL scheme. I recommend using `ALL` unless convenience for any scripts wanting to encrypt their Client ID with a service certificate manually.
you only want `CONTENT` keys and will not be using any other type of keys including `SIGNING` and `OPERATOR_SESSION`. - All License Keys from Serve's `/keys` endpoint can now be received by providing `ALL` as the key type.
- Serve now has a `/close` endpoint to close a session. The Cdm has a limit of 50 sessions per user. - This adds support for systems needing more than two types of keys from the license, e.g., Netflix MSL.
- Serve now responds with a `Server` header denoting that pywidevine serve is being used, also specifying the version. - For faster response times it is best to still ask for only `CONTENT` keys if that's all you need.
This allows Clients to selectively support APIs based on version, and also verify the API as being supported at all. - Serve now has a `/close` endpoint to close a session. All clients should close the session once they are finished
- Serve now verifies that all Devices in config actually exist before letting you start serving. with it or the user will eventually hit a limit of 50 sessions per user and the server will hog memory til it
restarts.
- Serve now verifies that all Devices in config actually exist before starting the server.
- Serve now responds with a `Server` header denoting that pywidevine serve is being used, and it's version.
- This allows Clients to selectively support APIs based on version; verify the API as being supported.
### Changed ### Changed
- Downgraded lxml to >=4.8.0 to support projects using pycaption, which is likely considering the project's topic. - Lessened version pin on `lxml` from `^4.9.1` to `>=4.8.0` to support projects using pycaption.
- Service Certificate is now saved in the session as a `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.
- Serve now constructs one Cdm object for each user+device combination so one user cannot fill or overuse the CDM
session limit.
- All of Serve's endpoints now have a `/{device}` prefix. E.g., instead of `/challenge/STREAMING`, it's now - All of Serve's endpoints now have a `/{device}` prefix. E.g., instead of `/challenge/STREAMING`, it's now
`/device_name/challenge/STREAMING`. This is to support a multi-device per-user Cdm setup, see Fixed below regarding `/device_name/challenge/STREAMING`. This is to support the previous change.
Serve's Cdm objects.
### Fixed ### Fixed
- Fixed support for Raw PSSH values, e.g., Netflix's WidevineExchange MSL Scheme arbitrary init_data value. - Handle server crash when the session limit is reached in Serve's `/open` endpoint by returning a 400 error.
- The Service Certificate is now saved to the Session in full SignedMessage form instead of just the underlying - Serve now correctly updates (or rather now makes a new Cdm object) if a user switches from one Device to another.
DrmCertificate. This is so any class inheriting the Cdm (e.g., for Remote capabilities) can sufficiently use - Previously it would reuse an existing Cdm object, but would forget to switch device if they changed.
and supply the service certificate while being signed. - Note: It does still leave the previous Cdm with the older Device in memory.
- Serve's /open endpoint will now return a 400 error if there's too many sessions opened. - Handle IOError when parsing bytes as MP4 Box to allow arbitrary data to be made as new boxes in `PSSH.get_as_box()`.
- Serve's Cdm objects with Device initialized are now stored per-user and device name. This fixes the issue where the
entire user base has only 50 sessions available to be used. Effectively rate limiting to only 50 users at a time.
Since /close endpoint was not implemented yet, there was no way to even close effectively meaning only 50 uses could
be done.
## [1.2.0] - 2022-07-30 ## [1.2.0] - 2022-07-30
### Added ### Added
- New CLI command `serve` to serve local WVD devices and CDM sessions remotely as a JSON API. - New CLI command `serve` that hosts a CDM API that can be externally accessed with authentication. This can be used to
- The CLI command `migrate` can now accept a folder path to batch migrate WVD files. access and/or share your CDM without exposing your Widevine device private key, or even it's identity by enforcing
- The Cdm now uses custom exceptions where the use case is justified. All custom exceptions are under a parent custom Privacy Mode.
exception to allow catching of any Pywidevine exception. - Requires installing with the `serve` extras, i.e., `pip install pywidevine[serve]`.
- The default host of `127.0.0.1` blocks access outside your network, even if port-forwarded. Use
`-h 0.0.0.0` to allow remote access.
- Setup requires the use of a config file for configuring the CDM and authentication. An example config file named
`serve.example.yml` in the project root folder has verbose documentation on available options.
- Batch migration of WVD files by passing a folder as the path to the CLI command `migrate`.
- Strict mode to `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.
### Changed ### Changed
- The Cdm has been reworked as a session-based Cdm. You now initialize the Cdm with just the device you wish to use, - Elevated the Development Status Classifier from 4 (Beta) to 5 (Production/Stable).
and now you open sessions with `Cdm.open()` to get a session ID. For usage example see `license` CLI command in - License messages passed to `Cdm.parse_license()` are now rejected if they are not of `LICENSE` type.
`main.py`. - Service Certificates passed to `Cdm.set_service_certificate()` are now verified. This patches a trivial "exploit"
- The Cdm no longer requires you to specify `raw` bool parameter. It now supports arbitrary and valid Widevine Cenc that allows an attacker to recover the plaintext Client ID from a license under Privacy Mode. See
Header Data without needing to explicitly specify which it is. <https://gist.github.com/rlaphoenix/74acabdd7269a21845e18b621c5860ef>.
- The Cdm `pssh` param has been renamed as `init_data`. Doc-strings have been changed to prioritize explanation of it - Data passed to `PSSH.get_as_box()` now supports arbitrary and box data automatically as it tries to detect if it is a
referring to Widevine Cenc Header rather than PSSH Boxes. This is to show that the Cdm more-so wants Init Data than valid box, otherwise makes a new box.
a PSSH box. The full PSSH is never kept nor ever used, only it's init data is. It still supports PSSH box data. - Renamed the `Cdm` constructor's parameter `pssh` to `init_data`, as that's what the Cdm actually wants and uses,
- Cdm `set_service_certificate()` now returns the provider ID string rather than the underlying (and now verified) whereas a `PSSH` is an `mp4` atom (aka box) containing `init_data` (a Widevine CENC Header). The full PSSH is never
DrmCertificate. This is because the DrmCertificate is not likely useful and would still be possible to obtain in full kept nor ever used. It still accepts PSSH box data.
but quick access to the Provider ID may be more useful. - Service Certificate's Provider ID is now returned by `Cdm.set_service_certificate()` instead of the passed
- License responses can now be only be parsed once by `Cdm.parse_license()`. Any further attempts will raise an certificate, of which they would already have.
InvalidContext exception. This is because context data is now cleared for it's respective License Request once it's - The Cdm class now works more closely to the official CDM model. Instead of using one Cdm object per-request having to
parsed to reduce data lingering in memory. provide device information each time,
- Trove Classifier for Development Status is now 5 (Production/Stable). - You now initialize the Cdm with the Widevine device you wish to use and then open sessions with `Cdm.open()`.
- You will receive a session ID that are then passed to other methods of the same Cdm object.
- The PSSH/init_data that used to be passed to the constructor is now passed to `Cdm.get_license_challenge()`.
- This allows initializing one Cdm object with up to 50 sessions open at the same time.
Session limits seem to fluctuate between libraries and devices. 50 seems like a permissive value.
- Once you are finished with DRM operations, discard all session (and key) data by calling `Cdm.close(session_id)`.
- License Keys are no longer returned by `Cdm.parse_license()` and now must be obtained directly from `cdm._sessions`.
- For example, `for key in cdm._sessions[session_id].keys: print(f"[{key.type}] {key.kid.hex}:{key.key.hex()}")`.
- This is to detach the action of parsing a license as just for getting keys, as it isn't. It can be and should be
used for a lot more data like security requirements like HDCP, expiration, and more.
- It is also to detour users from directly using the keys over the `Cdm.decrypt()` method.
- Various std-lib exceptions have been replaced with custom exceptions under `pywidevine.exceptions`.
- License responses can now only be parsed once by `Cdm.parse_license()`. Any further attempts will raise an
`InvalidContext` exception.
- This is as license context data is cleared once used to reduce data lingering in memory, otherwise the more license
requests you make without closing the session, the more and more memory is taken up.
- Open multiple sessions in the same Cdm object if you need to request and parse multiple licenses on the same device.
### Removed ### Removed
- You can no longer provide a direct `DrmCertificate` to `Cdm.set_service_certificate()` for security reasons. - Direct `DrmCertificate`s are no longer supported by `Cdm.set_service_certificate()` as they have no signature.
You must provide either a `SignedDrmCertificate` or a `SignedMessage` containing a `SignedDrmCertificate`. See the 3rd Change above. Provide either a `SignedDrmCertificate` or a `SignedMessage` containing a
- PSSH `from_init_data()` has been removed. It was unused and is unnecessary with improvements to `get_as_box()`. `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.
### Fixed ### Fixed
- Cdm `set_service_certificate()` now verifies the signature of the provided Certificate. This patches a trivial - Detection of Widevine CENC Header data encoded as bytes in `PSSH.get_as_box()`.
exploit/workaround that allows an attacker to recover the plaintext Client ID from an encrypted Client ID. - Custom ValueError on missing contexts instead of the generic KeyError in `Cdm.parse_license()`.
- Cdm `parse_license()` now verifies the input message type as a `LICENSE` message. - Typing of `type_` parameter in `Cdm.get_license_challenge()`.
- Cdm `parse_license()` now clears context for the License Request once it's License Response message has been parsed. - Value of `type_` parameter if is a string in `Cdm.get_license_challenge()`.
This reduces data lingering in the `context` dictionary when it may only be needed once.
- The Context Availability error handler in Cdm `parse_license()` has been fixed.
- Typing of `type_` param of `Cdm.get_license_challenge()` has been fixed.
## [1.1.1] - 2022-07-22 ## [1.1.1] - 2022-07-22
### Fixed ### Fixed
- The --vmp argument of the create-device command is now optional. - The `-v/--vmp` parameter of the `test` CLI command is now optional.
## [1.1.0] - 2022-07-21 ## [1.1.0] - 2022-07-21
### Added ### Added
- Added support for setting a Service Certificate in SignedDrmCertificate form as well as raw DrmCertificate form. - WVD (Widevine Device file) Version 2 bringing reduced file sizes by up to 30%~.
However, It's unlikely for the service to provide the certificate in raw DrmCertificate form without a signature. - New CLI command `create-device` to create `.wvd` files (Widevine Device files) from RSA PEM/DER Private Keys and
- Added a CLI command `create-device` to create Widevine Device (`.wvd`) 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. Client ID blobs. You can also provide VMP (FileHashes) data which will be merged into the Client ID blob.
- Added a CLI command `migrate` that uses `Device.migrate()` and `dump()` to migrate older v1 Widevine Device files - New CLI command `migrate` that uses `Device.migrate()` and `dump()` to migrate older v1 Widevine Device files to v2.
to v2. - New `Device` method `migrate()` to load an older Widevine Device file format. It is recommended to then use the
- Added the v1 Structure of Widevine Devices for migration use. `dumps()` method to save it as a new v2 Widevine Device file, which can then be loaded normally.
- Added `Device.migrate()` class method that effectively loads older format WVD data. You can then use `dumps()` to - Support `SignedDrmCertificate` and `DrmCertificate` messages in `Cdm.set_service_certificate()`. Services can provide
get back the WVD data in the latest supported format. the certificate as a `SignedMessage`, `SignedDrmCertificate`, or a `DrmCertificate`. Only `SignedMessage` and
- Added ability to use Privacy mode on the test command. `SignedDrmCertificate` are signed.
- Privacy Mode can now be used in the `test` CLI command with the `-p/--privacy` flag.
### Changed ### Changed
- Set Service Certificates are now stored as the raw underlying DrmCertificate as the signature data is unused by - Moved all `.wvd` Widevine Device file structures from `Device` to a `_Structures` class in `device.py`. The
the CDM. `_Structures` class can be imported and used directly, or via `Device.structures`.
- Moved all Widevine Device structures under a Structures class. - Moved the majority of Widevine Device file migration code from the CLI command `migrate` to `Device.migrate()`. The
- I removed the `send_key_control_nonce` flag from all Structures even though it was technically used. CLI command `migrate` now internally uses `Device.migrate()`.
This is because the flag was never used as of this project, and I do not want to take up the flag slot. - Set Service Certificates are now stored as `DrmCertificate`s instead of a `SignedMessage` as the signature and other
data in the message is unused and unneeded.
### Fixed
- Devices `dump()` function now uses the correct `type_` parameter when building the struct.
- Fixed release date year of v1.0.0 and v1.0.1 in the changelog.
## [1.0.1] - 2022-07-21
### Added
- More information to the PyPI meta information, e.g., classifiers, readme, some URLs.
### Changed
- Moved the License Type parameter from the Cdm constructor to `get_license_challenge()`.
- The Session ID is no longer used as the Request ID which could help with blocks or replay checks due
to it being the same Session ID for each request. It's now a random 16 byte value each time.
- Only the Context Data of each license request is now stored instead of the full message.
### Removed ### Removed
- Removed unnecessary and unused `raw` Cdm class instance variable. - Unused Widevine Device file flag `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.
### Fixed ### Fixed
- CDMs `set_service_certificate()` now correctly raises a DecodeError on Decode Error instead of a ValueError. - Correct the type argument name from `type` to `type_` in `Device.dump()`.
- Context Data will now always match to their corresponding License Responses. This fixes an issue where creating
a second challenge would overwrite the context data of the first challenge. Parsing the first challenge after ### Security
would result in either a key decrypt error, or garbage key data.
- Even though support for more kinds of Service Certificate Signatures were added, they are still unverified as the
signing public key is Unknown.
## [1.0.1] - 2022-07-21
### Changed
- Moved the License Type parameter from the `Cdm` constructor to it's `get_license_challenge()` method.
- Every License request now uses a unique random value instead of the CDM Session ID.
- Only the Context Data of License requests are now stored in the Session instead of the full message.
- Session ID formula now uses a random 16-byte value for both Chrome and Android provisions.
### Removed
- Unused and unnecessary `Cdm.raw` class instance variable.
### Fixed
- Re-raise DecodeErrors instead of a new ValueError on DecodeErrors in `Cdm.set_service_certificate()`.
- Creating a new License request no longer overwrites the context data of the previous challenge.
## [1.0.0] - 2022-07-20 ## [1.0.0] - 2022-07-20
Initial Release. Initial Release.
### Security
- Service Certificate Signatures are unverified as the signing public key is Unknown.
[1.6.0]: https://github.com/rlaphoenix/pywidevine/releases/tag/v1.6.0 [1.6.0]: https://github.com/rlaphoenix/pywidevine/releases/tag/v1.6.0
[1.5.3]: https://github.com/rlaphoenix/pywidevine/releases/tag/v1.5.3 [1.5.3]: https://github.com/rlaphoenix/pywidevine/releases/tag/v1.5.3
[1.5.2]: https://github.com/rlaphoenix/pywidevine/releases/tag/v1.5.2 [1.5.2]: https://github.com/rlaphoenix/pywidevine/releases/tag/v1.5.2