FairPlay Streaming
SuperDRM issues FairPlay licences (SPC → CKC) with Apple's FairPlay Streaming Server SDK 26 running as a local process, fronted by a small adapter that speaks SuperDRM's KSM contract. Nothing from Apple's SDK ships in our images or repository: it is downloaded from your Apple developer account, unpacked on the licence host, built there, and stays there. Apple devices always decrypt in the Secure Enclave path, so every FairPlay licence is a hardware licence.
Safari / iOS / tvOS ──SPC──▶ api.<domain> /v1/<tenant>/license/fairplay
│ opens the content key from the vault
▼
ksm-sidecar adapter 127.0.0.1:8781
│ Apple JSON: spc, content-key, content-iv, lease/offline, hdcp-type
▼
Apple fpssdk_server 127.0.0.1:8780 (built from the SDK; credentials/ next to it)
│ libfpscrypto: decrypt SPC, derive keys, build CKC
▼
CKC ──▶ back to the device#What is proven
| Layer | Proof |
|---|---|
| Apple's SDK builds and passes its own suite on Linux/arm64 | its regression tests pass |
| Adapter ↔ Apple server | all 106 SPC test vectors Apple ships (iOS, macOS Intel and Apple silicon, tvOS, visionOS, watchOS; lease, no-lease, offline; 1024- and 2048-bit; cbcs and cbc-ts) produced CKCs that Apple's own verifier decrypts and matches |
| Through the SuperDRM API | a tenant with Apple's test key imported; POST /v1/<tenant>/license/fairplay?lt=… with the iOS SPC → 200, a 604-byte CKC, meter row fairplay · hw-main · ok · 9 ms |
| In production | the hosted service runs with production credentials (/health → fairplay_ksm ok · production credentials) and serves SuperSecure's Safari and iOS viewers |
#The adapter contract
POST /ksm { spc: b64, key: b64(16), iv: b64(16), asset_id, policy } → { ckc: b64, client, ms, mode }
| SuperDRM policy | Apple asset-info |
|---|---|
lease_duration_seconds > 0 | lease-duration (seconds from SPC creation) |
persistence: true + rental_duration_seconds, playback_duration_seconds | offline-hls { stream-id, title-id, rental-duration, playback-duration } |
hdcp: none / type0 / type1 | hdcp-type: -1 / 0 / 1 |
content_type (default hd; audio, sd, hd, uhd) | content-type |
encryption_scheme (default cbcs; cbc-ts) | encryption-scheme |
client carries what Apple's KSM learned from the SPC: hu (anonymised device id), security_level (baseline or main), device_class, os_version, hdcp_capable, offline_capable. The api records security = hw-<level> and a hash of hu on the meter row. Only the CKC ever goes to the device.
GET /health on the adapter → { ok, mode: test|production, sdk: "26.0.4" }.
#Production credentials (your own Apple package, for an on-prem install)
Apple issues a certificate bundle and provisioning data against two certificate signing requests.
Generate keys and CSRs on the licence host, in a directory with mode 0600:
Shell openssl req -out csr_1024.csr -new -newkey rsa:1024 -nodes -keyout priv_key_1024.pem \ -subj "/CN=<your company>/OU=DRM/O=<your company>/C=US" openssl req -out csr_2048.csr -new -newkey rsa:2048 -nodes -keyout priv_key_2048.pem \ -subj "/CN=<your company>/OU=DRM/O=<your company>/C=US"If a 1024-bit key was already used with FairPlay, Apple lets you reuse that certificate and submit only the 2048-bit CSR.
developer.apple.com → Certificates, IDs & Profiles → Certificates → + → FairPlay Streaming Certificate → select the current SDK version → upload
csr_2048.csrunder 2048-bit andcsr_1024.csrunder 1024-bit (or "reuse previous certificate") → Continue → Downloadfps_bundle.zip.fps_bundle.zipcontainsfps_certificate.bin(served to clients, and read by the KSM) andprovisioning_data.bin(KSM only). Copy both plus the two.pemfiles into the server'scredentials/folder and writecredentials/certificates.json:JSON { "certificates": [ { "certificate": "fps_certificate.bin", "1024-private-key": "priv_key_1024.pem", "2048-private-key": "priv_key_2048.pem", "provisioning-data": "provisioning_data.bin" } ] }Several bundles may coexist in the array; the KSM picks by the SPC header's certificate hash.
Build Apple's server for production (without the
test_credentialsfeature):cargo build --release --bin fpssdk_server, install it next to the adapter, setFPS_CREDENTIALS_MODE=production, restart theksmservice. Then serve the certificate to clients:Shell superdrm tenant:drm <slug> fairplay --mode shared --public '{"cert_b64":"<base64 of fps_certificate.bin>"}'
Notes from Apple's guide: certificates are not backward-compatible (use with the matching SDK or newer); an SDK 26 certificate enables SPC version 3 and the highest video-key protection; an older certificate makes SDK 26 behave like SDK 5.x.
#Older (SDK 4-style) credentials
certificates.json also accepts {"certificate": "<the .der/.cer>", "1024-private-key": "<its key>", "ask": "<16-byte ASk as 32 hex chars>"} — no provisioning data needed for that generation. Use it if you hold the private key and Application Secret Key from an earlier FairPlay deployment.
#Operations
| Service | Role | Listens |
|---|---|---|
fps | Apple fpssdk_server (FPS_BIND, FPS_CERT_PATH) | 127.0.0.1:8780 |
ksm | the adapter (KSM_TOKEN, FPS_CREDENTIALS_MODE, FPS_CONTENT_TYPE) | 127.0.0.1:8781 |
| api | SUPERDRM_FPS_KSM_URL=http://127.0.0.1:8781, SUPERDRM_FPS_KSM_TOKEN | — |
Apple's reference server parses Content-Length case-sensitively; the adapter therefore talks to it with an explicitly cased header. Containers: the Compose profile ksm builds ksm-sidecar/Dockerfile, which expects the SDK's Key_Server_Module copied to ksm-sidecar/vendor/ (git-ignored) and the credentials mounted at /fps. In hardened tiers the pair runs inside an enclave; the contract is the same.
#Bridging to a previous vendor
A tenant whose keys only a previous vendor holds can run tenant_drm.fairplay mode=proxy, which relays the SPC to that vendor's FairPlay endpoint until the keys are imported. Once SuperDRM holds a key (issued or imported), --mode shared licenses it with the local KSM — the KSM does not depend on who generated the key.
Updated September 2026