System Design Masterclass · Module 23 / Week 4

DRM & Content Security

The layer where your platform's money lives. The DRM triad and CDM security levels, the license request path as a latency-critical distributed system, key rotation for live, and the defense stack beyond DRM — tokens, watermarking, and concurrency enforcement.

~2.5h study~1h exercise1 interactive simFast track ~70 min

Fast track — living with Widevine already?

Compresses to ~70 min. The ecosystem overview collapses; the license path (your 428 incident class lives here), key handling for live, and the defense-in-depth stack stay mandatory.

  1. §01 takeaway · 10 min
  2. §02 license-path sim + failure design, in full · 25 min · MANDATORY
  3. §03 key rotation, in full · 15 min · MANDATORY
  4. §04 beyond DRM, in full · 15 min · MANDATORY
STEP 01

The stack in one pass

SKIM
Fast-track takeawayContent is AES-encrypted once (CENC/CBCS common encryption — one set of encrypted segments serves all DRMs; CBCS for the widest device span including Apple); the fight is over key delivery. The triad — Widevine (Google estate), FairPlay (Apple), PlayReady (Microsoft/TVs) — because each platform trusts only its own CDM (Content Decryption Module: the black box in the device that holds keys and decrypts, ideally in hardware). Security levels are business logic: Widevine L1 (hardware TEE) vs L3 (software) — studios contractually gate content by level (4K/HDR requires L1 + HDCP), so device capability → allowed quality is a policy matrix your entitlement service owns, and "why is this phone SD-only" tickets are this matrix working as agreed. Your leverage points as the platform: the license service policy, the token chain feeding it, and the multi-DRM packaging pipeline — the CDMs themselves are non-negotiable vendor territory.
STEP 02

The license path — a latency-critical distributed system

MANDATORY

Fast trackFull read + sim. This path sits between "user presses play" and "video appears" — every module of Weeks 2–3 applies to it, and its failures are your SMIL_ERROR_LICENSE_NOT_GRANTED family.

Press play → first frame
1
Player requests playback → playback/session service: authZ (M22 token), concurrency check, returns manifest URL + license-service token
2
Player fetches manifest + init segment from CDN (M10) — sees content is encrypted, extracts key IDs (PSSH)
3
CDM generates a license challenge (device-attested); player POSTs it + your token to the license service
4
License service: validate token → entitlement decision (plan, rental window, region, device level) → fetch/derive content keys → build policy-bound license (expiry, HDCP, level)
5
CDM verifies, installs keys, decrypts — first frame renders
Steps 1–4 are all before first frame: the license path is inside your startup-time SLO. Run the failure to see your incident class.
STEP 03

Keys, rotation & live

MANDATORY
STEP 04

Beyond DRM: the honest defense stack

MANDATORY

DRM protects segments-at-rest-and-in-flight; real-world piracy mostly goes around it (screen capture, HDMI strippers, credential abuse, restream). The stack that addresses what DRM can't:

Staff expectationTalking about content security as economics, not absolutes: every layer raises the pirate's cost or lowers the leak's value (rotation shortens usable life; watermarking adds identification risk; tokens remove the free-rider tier). The design review question is "which attack does this layer price out, and what does it cost us in latency/cacheability/complexity?" — the same trade-off discipline as the rest of the course, applied to adversaries. And the license path is a Tier-0 service: it gets the same capacity, resilience, and game-day treatment as playback itself, because to the user they are the same thing.
STEP 05

Exercise

MANDATORY

Fast trackStep 2 (~30 min): the paper redesign of your real license path against the Week-2/3 checklist is the highest-value hour in this module.

1
Build a toy license service. Clear-key DRM (no vendor needed): Shaka packager encrypts a sample with CENC; a Spring service validates a JWT (M22), checks a mock entitlement (with an M01-style cache + event invalidation), and returns keys; Shaka player plays it. Add the error taxonomy (403-not-entitled vs 428-precondition vs 503-transient) and per-device rate limiting.
2
Audit the real path. Map your production press-play→first-frame sequence step by step (§02 as the template). For each hop: capacity at match-start herd, cache strategy + invalidation trigger, breaker/fallback policy, idempotency, error code surfaced to the player. Every blank cell is a pre-written incident — including, probably, the one you already had.
3
Paper — rotation runbook. Write the live key-rotation section of your event-readiness runbook: signaling lead time, expected license-request surge profile, the abort criteria, and the mid-event revocation procedure.

Self-check

Why does 4K-on-L1-only need to be enforced by key separation, not just license policy?
If all renditions share one key, any licensed device (including an L3 software CDM, more easily compromised) receives the key that also unlocks UHD segments — policy said SD, cryptography delivered everything. Per-tier keys mean an L3 license physically contains only SD/HD keys: the contract with studios is enforced by what keys exist where, surviving even a compromised CDM at the low tier.
Kickoff license storm: walk the mitigations in order of leverage.
(1) Pre-delivery: let players acquire licenses at lobby/pre-roll time, spreading minutes of load ahead of second zero (policy permitting). (2) Jittered acquisition in the SDK (M01's jitter-for-humans). (3) Entitlement decisions from event-invalidated cache, not origin lookups (M01/M10). (4) License-service capacity from M02 math at assumed worst concurrency + retry factor, bulkheaded from VOD licensing (M13). (5) Per-device limits catching firmware loops before they add to the storm (M14). Same anatomy as the manifest herd — because it is one.
Your 428/SMIL_ERROR_LICENSE_NOT_GRANTED spike: using this module, name the three most likely systemic causes and their fixes.
(1) Entitlement cache staleness — grant written, cache invalidation event lost/delayed (M18 dual-write on the invalidation path!): fix with outbox-driven invalidation and read-your-writes on fresh purchases (M04). (2) Race between purchase saga completion and first license attempt — player retries before the retriable post-pivot grant step lands (M17): fix with player retry-with-backoff on this specific code + saga-state-aware responses (202-pending, not 428-denied). (3) Token/claim staleness — plan tier as a JWT claim not yet refreshed (M22): force refresh on purchase events. All three are curriculum, which is why the RCA wrote itself once the model existed.
Forensic watermarking threatens CDN cacheability. How do real systems square it?
Per-session unique streams would shatter the cache (M10's fragmentation trap). A/B watermarking: encode two (or few) watermark-variant copies of each segment; per session, serve a unique *sequence* of A/B choices (the bit pattern = the session ID). Cache stores only 2× segments (fully shared); uniqueness lives in the manifest/path selection layer. Identification strength comes from sequence length — a few minutes of stolen stream yields the session.