Attested devices

Remote attestation is SuperDRM's answer to platforms with no hardware CDM: instead of trusting the client's software stack, the licence server verifies — cryptographically, against the machine's TPM — that the device booted an exact, registered OS image before any key is released. It is the same root-of-trust argument Android's Widevine L1 makes (vendor-controlled boot chain plus attestation), built from measured boot, and it gates our licence issuance, so it works for any key system including Clear Key served to a native player.

#Trust model

  • The device's TPM holds an attestation key (AK) — a restricted signing key that can only sign what the TPM itself measured. Enrollment pins the AK's public half.
  • Boot measurements land in PCRs (Secure Boot state in 0/7, the signed unified kernel image — kernel, initrd, cmdline, dm-verity root hash — in 11). An image registers the golden PCR values of one viewer release.
  • A licence request under policy.attested needs a fresh attestation token, minted only after the device returns a TPM quote — over a single-use, device-bound, 2-minute nonce — whose signature verifies against the enrolled AK and whose PCRs equal the golden set.
  • Booting anything else changes the PCRs; the quote no longer matches; no key. There is no software path around it on any OS the user can boot.

#Endpoints

Management (tenant API key):

EndpointBodyDoes
POST /v1/attest/devices{name?, ak_pub, ek_pub?} (base64 of tpm2_createak -u / tpm2_createek -u)Enroll a device. Refuses non-restricted keys. → {device, ak_fingerprint}
GET /v1/attest/devicesList devices with last_attested_at.
POST /v1/attest/devices/:id/revokeKill a device: its tokens stop working at the next licence request.
POST /v1/attest/images{label, golden: {"0":"<hex>","7":"<hex>","11":"<hex>"}}Register or replace a release's golden PCRs.
GET /v1/attest/imagesList images.

Device-facing (open, per tenant — the quote is the credential):

EndpointBodyDoes
POST /v1/<tenant>/attest/challenge{device}{nonce} (single-use, 120 s, bound to the device).
POST /v1/<tenant>/attest/verify{device, image, quote_b64, sig_b64, pcrs}Verifies AK signature, nonce, PCR digest, golden match. → {token, exp} (15 min HS256) or a 403 whose error lists every failed check.

Licence integration: mint the licence token with policy: {attested: true} (or set it on the tenant or webhook policy). The player sends the attestation token as X-SuperDRM-Attestation (or ?at=). Every gated request re-checks device status, so revocation is immediate. Issued licences carry attested in the meter row and the live event stream.

#Device side

scripts/attest-client.sh is the whole client — plain tpm2-tools plus curl, no daemon:

Shell
# once, on the device (mints EK+AK in the TPM, registers the AK)
./attest-client.sh enroll sdrm_live_…            # → {"device":"<id>", …}

# once per image release, from a golden boot (prints the body for POST /v1/attest/images)
./attest-client.sh golden

# each session: challenge → tpm2_quote → verify → token on stdout
./attest-client.sh token <device_id> viewer-1.0.0

Defaults: TPM2TOOLS_TCTI=device:/dev/tpmrm0, PCRs 0,7,16 (16 is the resettable debug PCR — use it for bring-up; production images measure into 11 via systemd-stub and set ATTEST_PCRS=0,7,11). Point TPM2TOOLS_TCTI=swtpm:host=127.0.0.1,port=2321 at a software TPM to rehearse without hardware.

#Verifier notes

The verifier parses the TPM's own wire structures (TPM2B_PUBLIC, TPMS_ATTEST, TPMT_SIGNATURE; ECC P-256/ECDSA and RSA/RSASSA, SHA-256) — no tpm2 binaries on the server. The verdict checks, in order: enrolled-AK signature over the message, nonce equality (burned before the verdict so a failing quote consumes it), the submitted PCR values against the TPM-signed digest, then each golden PCR. A refusal returns all failed checks, so a fleet device that drifts can be diagnosed from the audit log (attest.pass / attest.fail).

Proven end to end against a real AMD firmware TPM and against swtpm in CI: genuine boot issued, tampered measurement refused, replayed quote refused, revoked device refused mid-token.

Updated September 2026