mirror of
https://github.com/unshackle-dl/unshackle.git
synced 2026-11-12 20:15:10 +00:00
feat(remote): verify server-CDM vault keys on the client and report bad pairs
The licence response lists KIDs a server vault supplied, the client proves them with the decode check, and a bad pair goes to the new session keys/bad route so the server flags its own row. Client vault keys go first over --remote, and the dashboard health warns when no SQLite vault can store flags.
This commit is contained in:
@@ -210,8 +210,10 @@ missing or a vault's credentials expired. This finds that before a job does.
|
||||
`status` is `ok`, `degraded` (an optional dependency is missing) or `failing` (a required one
|
||||
is). Player binaries are not checked: a headless server never has one and it plays nothing.
|
||||
Each check is `ok`, `warn` or `fail`. Check ids are the binary names, `cdm`,
|
||||
`vault:<name>` (the vault's configured `name`, so two vaults of the same type stay apart), and
|
||||
`proxies`.
|
||||
`vault:<name>` (the vault's configured `name`, so two vaults of the same type stay apart),
|
||||
`bad_keys` and `proxies`. `bad_keys` warns when the vaults hold no `SQLite` entry, because only
|
||||
that backend stores the flag for a content key a remote client proved wrong; without it the
|
||||
server serves the same bad pair again.
|
||||
|
||||
The server caches the result for 30 seconds and keeps every probe shallow: a vault answers one
|
||||
lookup that should miss, and no probe allocates anything. A failing probe's `detail` has the
|
||||
|
||||
@@ -849,6 +849,7 @@ POST /api/session/{id}/tracks → tracks + chapters (with URLs) for one title
|
||||
POST /api/session/{id}/segments → resolve segment URLs / DRM / headers per track
|
||||
POST /api/session/{id}/segment_filter → HLS segments the service drops (ads, bumpers)
|
||||
POST /api/session/{id}/license → proxy or server-CDM DRM licensing
|
||||
POST /api/session/{id}/keys/bad → report a server-vault content key that did not decrypt
|
||||
DELETE /api/session/{id} → tear down, harvest updated cache
|
||||
```
|
||||
|
||||
@@ -1058,13 +1059,13 @@ Get the content keys for the DRM. The `mode` field selects one of two modes.
|
||||
=== "Single: Response `200`"
|
||||
|
||||
```json
|
||||
{ "keys": { "<kid_hex>": "<key_hex>" } }
|
||||
{ "keys": { "<kid_hex>": "<key_hex>" }, "vault_keys": ["<kid_hex>"] }
|
||||
```
|
||||
|
||||
=== "Batch: Response `200`"
|
||||
|
||||
```json
|
||||
{ "keys": { "v-1": { "<kid_hex>": "<key_hex>" } }, "drm_type": "widevine" }
|
||||
{ "keys": { "v-1": { "<kid_hex>": "<key_hex>" } }, "vault_keys": ["<kid_hex>"], "drm_type": "widevine" }
|
||||
```
|
||||
|
||||
| Field | Description |
|
||||
@@ -1074,6 +1075,7 @@ Get the content keys for the DRM. The `mode` field selects one of two modes.
|
||||
| `pssh` | Base64 PSSH (server-CDM mode). |
|
||||
| `drm_type` | `widevine` (default) or `playready`. |
|
||||
| `mode` | `proxy` (default) or `server_cdm`. |
|
||||
| `vault_keys` (response) | KIDs whose content key came from a server vault, not the CDM. The client proves such a content key with a decode before it trusts it; the field is absent when every content key came from the CDM. |
|
||||
|
||||
| Status | Error code | Meaning |
|
||||
| --- | --- | --- |
|
||||
@@ -1084,6 +1086,20 @@ Get the content keys for the DRM. The `mode` field selects one of two modes.
|
||||
| `404` | `NO_CONTENT` | Server CDM produced no keys. |
|
||||
| `502` | `SERVICE_ERROR` | The service aborted during licensing. |
|
||||
|
||||
### `POST /api/session/{session_id}/keys/bad`
|
||||
|
||||
Report a content key from `vault_keys` that did not decrypt the track. The server flags the pair in its local vaults, under the name of the vault that served it, and the next licence for that KID reaches the CDM. The client never learns the server vault names.
|
||||
|
||||
```json
|
||||
{ "kid": "<kid_hex>", "key": "<key_hex>" }
|
||||
```
|
||||
|
||||
| Status | Error code | Meaning |
|
||||
| --- | --- | --- |
|
||||
| `200` | - | The pair is flagged. |
|
||||
| `400` | `INVALID_INPUT` | This remote session was not served that pair. |
|
||||
| `404` | `SESSION_NOT_FOUND` | Unknown remote session. |
|
||||
|
||||
### `GET /api/session/{session_id}`
|
||||
|
||||
Return remote session metadata and validity.
|
||||
|
||||
@@ -197,6 +197,7 @@ request and response in full.
|
||||
| `POST` | `/api/session/{session_id}/segments` | Per-track segment/download details. |
|
||||
| `POST` | `/api/session/{session_id}/segment_filter` | Server-side HLS segment filter results for one track. |
|
||||
| `POST` | `/api/session/{session_id}/license` | Proxy or server-side DRM licensing. |
|
||||
| `POST` | `/api/session/{session_id}/keys/bad` | Report a server-vault content key that did not decrypt. |
|
||||
| `GET` | `/api/session/{session_id}/logs` | Drain the service's server-side log output. |
|
||||
| `GET` | `/api/session/{session_id}/prompt` | Poll for interactive auth status/prompt. |
|
||||
| `POST` | `/api/session/{session_id}/prompt` | Submit an answer to an auth prompt. |
|
||||
|
||||
@@ -236,6 +236,17 @@ There are two ways DRM keys get resolved, chosen by the client's `server_cdm` fl
|
||||
- Enable with `server_cdm: true` in the server entry.
|
||||
- The server tells the client which DRM type it used
|
||||
(`widevine` or `playready`).
|
||||
- A content key the server took from its own vault is unproven. The response
|
||||
lists its KID in `vault_keys`, the client decrypts and checks that the output
|
||||
decodes, and a wrong pair goes back to `POST /api/session/{id}/keys/bad`. The
|
||||
server flags the pair in every local vault it holds, records the name of the
|
||||
vault that served it, and the next licence for that KID skips the pair and
|
||||
reaches the CDM. A server with no local SQLite vault cannot store the flag. The
|
||||
server accepts a report only for a pair it served to that remote session. The
|
||||
client never learns the server vault names.
|
||||
- A content key in the client's own vaults goes first, even when the server
|
||||
already answered. The client proves it, and a server key waits as the next
|
||||
candidate.
|
||||
|
||||
---
|
||||
|
||||
@@ -313,6 +324,7 @@ The server mounts all these routes, even in `--remote-only` mode. Paths use the
|
||||
| `POST` | `/api/session/{id}/segments` | Resolve per-segment/track download descriptors |
|
||||
| `POST` | `/api/session/{id}/segment_filter` | Unwanted HLS segment URIs for one track (ads, bumpers) |
|
||||
| `POST` | `/api/session/{id}/license` | DRM licensing (proxy or server CDM) |
|
||||
| `POST` | `/api/session/{id}/keys/bad` | Report a server-vault content key that did not decrypt |
|
||||
| `GET` | `/api/session/{id}/logs` | Drain the service's server-side log output |
|
||||
| `GET` | `/api/session/{id}/prompt` | Poll interactive auth status / pending prompt |
|
||||
| `POST` | `/api/session/{id}/prompt` | Submit an answer to a pending prompt |
|
||||
|
||||
Reference in New Issue
Block a user