Commit Graph

255 Commits

Author SHA1 Message Date
rlaphoenix 7640d6fcab Bump to v1.3.0 2022-08-04 05:56:38 +01:00
rlaphoenix 3d794ad659 RemoteCdm: Implement /set_service_certificate 2022-08-04 05:54:15 +01:00
rlaphoenix 5788dde7b1 serve: Implement /set_service_certificate
Removed service certificate setting related code from /challenge.
2022-08-04 05:54:15 +01:00
rlaphoenix ddf755f82f Cdm: Add ability to unset certificate via set_service_certificate()
To unset, just provide `None` as the certificate param.
2022-08-04 05:43:10 +01:00
rlaphoenix e8785fcd84 Create RemoteCdm class as Client code for the `serve` feature
This can be considered the Client-side code for the `serve` feature.

The RemoteCdm object can be used with the same underlying interface as the normal `Cdm` object. Including stuff like .open(), .get_license_challenge(), .decrypt(), even same access to data like `cdm.system_id`, or even `cdm._sessions` just like normal.

However, since we don't have any private key and client ID, we spoof the super construction with dummy data. You wont have access to any data that uses the underlying Client ID and Private Key like the signer or decrypter. Any Cdm code trying to access them on RemoteCdm will fail.
2022-08-04 05:43:10 +01:00
rlaphoenix c969d80931 Cdm: Change construction interface to allow manual creation
This is so you can construct a Cdm object without using `.wvd` files (nor the Device class). It also improves enforcement of some required data from the Device. The underlying Device object is discarded for it's data as it won't be required.

Note that the Client ID and Private Key related variables are now stored as private `__var` variables to further amplify their private nature and to really discourage manual read write. This is not impossible to workaround in Python but further discourages manual read/writes to the variable that could cause serious issues.

The RSA Key is also no longer stored as-is. It is now stored as PSS and PKCS1_OAEP objects, as they will be used like so. This makes it even more annoying to directly read/write the RSA key (but not impossible).
2022-08-04 04:52:26 +01:00
rlaphoenix f1a38d1966 Update Changelog for v1.2.1 2022-08-02 01:53:44 +01:00
rlaphoenix 3fe87f2917 Bump to v1.2.1 2022-08-02 01:53:38 +01:00
rlaphoenix dc48c11e1a Add Changelog to PyPI Project URLs 2022-08-02 01:53:29 +01:00
rlaphoenix 97126391c4 PSSH: Fix get_as_box parsing on arbitrary init data
An IOError can occur if the mp4 box parsing fails because it could not read enough bytes.
2022-08-02 01:48:49 +01:00
rlaphoenix 6a286a4c23 Remove second serve dependencies check
The second one isnt needed so long as the YAML import is 2nd. Once it tries to import serve it will fail and it's ImportError will get handled.
2022-08-02 01:48:48 +01:00
rlaphoenix 4bc0edcca9 serve: Set `Server` response header with pywidevine version
This allows clients to test with a HEAD request to / to see what version the API is running and test if it's actually a pywidevine serve API.
2022-08-02 01:48:48 +01:00
rlaphoenix 4f96ee402b serve: Add check that all devices in config exist 2022-08-02 01:48:48 +01:00
rlaphoenix 2ba13f5e07 serve: Add /close endpoint
All client's should implement this and handle the 400 response safely. Under normal circumstances, with good client code, the 400 responses should not happen.
2022-08-02 01:48:48 +01:00
rlaphoenix a4d8be683b serve: add /{device} prefix to all endpoints
This is necessary to support different Cdm devices per-user. E.g., without this change if you do /open/a_device, you will only ever be able to use `a_device` until the next server restart. Even if you do /open/b_device, it will still use `a_device`, without error or warning.

This is because it stores the device with the Cdm in the previous change from storing the session ids to storing the Cdms instead.

With this change we can now have the user specify which device they are using, which allows us to map that to a Cdm that was initialized with the respective device.

Arguably we could remove the /{device} prefix and instead do a brute check on the app["cdms"] until we find a Cdm with a matching session, but this seems like a more semantic less hacky method to the madness.

(especially since /open already used {device}, but as a postfix)
2022-08-02 01:48:48 +01:00
rlaphoenix 9501c34f60 serve: Store Cdm per-secret, ensure session more efficiently
The Cdm is now stored per-secret due to the Cdm object's session limit. This is so one user (by secret key) cannot overload the server with too many sessions.

But this also fixes it so that the serve API will work for more than just 50 sessions for all users. Otherwise the user pool will eventually overload the Cdm with 50 sessions, even if they close it, it will eventually happen. Think of it like the server being overloaded prematurely.
2022-08-02 01:48:48 +01:00
rlaphoenix 290da707ea serve: Add ability to get all types of keys in /keys 2022-08-02 01:48:48 +01:00
rlaphoenix 64ae5709d3 serve: Handle TooManySessions on /open 2022-08-02 01:48:48 +01:00
rlaphoenix 5c1b0e89ef Cdm: Support multiple forms of Service Certs in encrypt_client_id 2022-08-02 01:48:48 +01:00
rlaphoenix 0c85abb2d4 Cdm: Save Service Certificate in SignedMessage form
We may need the signature for external verification, and most APIs require it to be in a SignedMessage to be accepted, even though the SignedMessage is pretty much empty (not even actually signed lol).
2022-08-02 01:48:48 +01:00
rlaphoenix a0fa559255 deps: Downgrade lxml to >=4.8.0
This is to add support with projects that likely use pycaption which does not yet support lxml 4.9.0 or newer.
2022-07-31 06:33:18 +01:00
rlaphoenix 3e1ccaf5ba Add correct changelog relating to serve command on v1.2.0 2022-07-31 01:32:39 +01:00
rlaphoenix 17384a8908 Bump to v1.2.0 2022-07-30 22:15:18 +01:00
rlaphoenix 7bb9ebf8f7 Update Changelog for v1.2.0 2022-07-30 22:14:59 +01:00
rlaphoenix e36411cfaf Cdm: Clear context for the challenge once loaded
This stops users from loading the license twice, which wouldn't do anything wrong, but without doing this context deletion we could possibly end up with a ton of memory that would likely go unused if the same Cdm session is used a lot for a long time.
2022-07-30 05:13:30 +01:00
rlaphoenix d744ed4c90 Update serve for Cdm changes, add /open endpoint
I've moved the majority of Cdm initialization from /challenge to /open, this is pretty much necessary to have a proper session setup like Cdm now has.

A session setup is required for an API like this to know what cdm to associate user's calls with. The session ID it uses is now the same session ID it actually uses in the Cdm but it's returned to the user as hex. The user is expected to provide it in hex as well.
2022-07-30 05:08:30 +01:00
rlaphoenix c7ec596031 Update license CLI command for Cdm changes 2022-07-30 04:50:18 +01:00
rlaphoenix 3536caf5f9 Rework Cdm as a Session Key/Store Cdm
There's a few benefits to this but the main one being storage for each "request". We can now change Service Certificate per-session for example rather than for the entire Cdm object. In a multi-threaded scenario this can be a necessity more than anything.

The device is the only bit of data left that does not get stored in a session. This is mostly due to myself not seeing it being switched out often and setting it per-session would likely be cumbersome.

Some other small improvements are all around. There's a ton of doc-string improvements, typing improvements, verification of types, and there's now custom Exceptions.

In terms of bug fixes there isn't any I fixed explicitly but a possible issue in decrypt() relating the Key Labels may now be fixed.

I've moved the Keys from the return of parse_license() to the session data, with decrypt() now loading them from the session data instead. This keeps the decryption keys out of the view of the caller but it is by no way impossible to get those keys. It is incredibly trivial to access the session and get the keys from the Cdm manually.

A session limit of 50 is still set by the Cdm.
2022-07-30 04:50:18 +01:00
rlaphoenix 58186de464 Create Exceptions 2022-07-30 04:50:17 +01:00
rlaphoenix 999900278f Create a Session class 2022-07-30 04:31:03 +01:00
rlaphoenix 82d99d50d0 Cdm: Fix typing of type_ param on get_license_challenge()
`LicenseType` shouldn't be used as a type-hint as its not a Type.
2022-07-30 04:22:35 +01:00
rlaphoenix 3afcf9c01c Cdm: Improve readability of license signature exception 2022-07-30 03:13:58 +01:00
rlaphoenix 3a15c1050a Cdm: Fix context availability check in parse_license() 2022-07-30 03:11:21 +01:00
rlaphoenix 71a43a069d PSSH: Fix mistake in the doc-string of get_as_box() 2022-07-30 02:56:22 +01:00
rlaphoenix 0bfbbdccc3 Cdm: Return the service cert provider id instead of the cert
There's no need for the user to get back the verified DrmCertificate as they could easily get it themselves. Instead return the provider ID which may be more useful to get.
2022-07-30 02:50:22 +01:00
rlaphoenix d1974ad1fb Cdm: Improve parsing of service certificates 2022-07-30 02:44:34 +01:00
rlaphoenix 7078759cdf Remove uses of raw from CLI commands and serve 2022-07-30 02:29:20 +01:00
rlaphoenix 1cedba7e49 Cdm: Change param pssh to init_data
This is to signal what the Cdm really uses. Asking for a PSSH may sound like it uses a full PSSH when in reality all it cares for is the underlying init data (Widevine Cenc Header/WidevinePsshData).
2022-07-30 02:26:11 +01:00
rlaphoenix b5ac0f45a2 Remove Cdm raw param, Improve PSSH.get_as_box()
The Cdm no longer requires you to specify if it's raw or not thanks to changes in PSSH.get_as_box() now supporting both dynamically.

It will parse the data and if its not a box, it will use the provided data in a newly crafted box.
2022-07-30 02:21:19 +01:00
rlaphoenix 8f7cacb10a PSSH: Remove from_init_data()
This is unused and will soon be unnecessary.
2022-07-30 02:21:02 +01:00
rlaphoenix 676110c01e PSSH: Fix check of Cenc Header data in get_as_box() 2022-07-30 01:33:21 +01:00
rlaphoenix a3102ded18 Cdm: Verify Signatures of Security Certificates
This improves Cdm security and prevents a trivial exploit on Privacy Mode allowing an attacker to bypass Privacy Mode by controlling their own Public/Private Key Pair on Service Certificates.

The attack is simple in which you create your own RSA-2048 key pair, replace the public key of a service certificate with your own, and now you have the corresponding private key to be able to decrypt Encrypted Client IDs. This trivial attack is often used on CDM re-implementations, proxies, and APIs to obtain sensitive Device Client ID information.

With this commit this attack is prevented on this Cdm implementation, making it more secure from attacks. A signed DRM Certificate must be provided now as the ability to provide a direct DrmCertificate has been removed.

The root certificate added alongside this commit has no private key and cannot be used to re-sign an altered DrmCertificate.
2022-07-29 22:14:48 +01:00
rlaphoenix d9d8074f73 Extend functionality of migrate cmd to folders of wvds
This is so you can mass migrate devices instead of painfully one by one.
2022-07-29 19:29:39 +01:00
rlaphoenix fc9a290482 Device: Move structure revision notes next to the structures 2022-07-25 00:13:33 +01:00
rlaphoenix f63b94c31d Add ability to serve cdm devices remotely with serve command 2022-07-24 21:48:09 +01:00
rlaphoenix ac469383b8 Cdm: Validate License Message type in parse_license 2022-07-24 21:07:00 +01:00
rlaphoenix b081d66ca2 Update Development Status Trove classifier 2022-07-23 17:03:11 +01:00
rlaphoenix aaf2362634 Fix exclude pattern of license proto on DeepSource
Seems this ** way didn't work for whatever reason.
2022-07-23 17:00:27 +01:00
rlaphoenix 683c3360a5 Improve the Disclaimers, limit to 5 disclaimers 2022-07-23 16:36:11 +01:00
rlaphoenix 93cdc7f44e Remove f-string without expression, mute unused variable in Cdm 2022-07-23 16:29:28 +01:00