Skip to content

Add capability preflight, with content protection as the first probe type - #29

Open
deisenbacher wants to merge 2 commits into
moq-wg:mainfrom
deisenbacher:preflight-contentprotection
Open

deisenbacher wants to merge 2 commits into
moq-wg:mainfrom
deisenbacher:preflight-contentprotection

Conversation

@deisenbacher

@deisenbacher deisenbacher commented Oct 15, 2026 •

Copy link
Copy Markdown

Add capability preflight, with content protection as the first probe type

Proposed by @deisenbacher. This PR is now the home of the capability preflight
proposal, replacing moq-wg/msf#214. Following review there (@wilaw), the whole
mechanism lives in CMSF, where it has direct use, instead of in MSF. It does
not depend on any MSF change.

Summary

This PR adds an optional preflight exchange to CMSF. Before a publisher
issues authorization for certain tracks, the subscriber reports its decoding,
display and content protection capabilities. The publisher then authorizes
exactly the tracks the subscriber can, and is permitted to, consume.

It fills a gap in MSF, which says token acquisition is out of scope. Preflight
is an optional, in-band way to acquire a token whose scope reflects the
subscriber's verified capabilities.

The mechanism is written to be independent of DRM: the catalog fields,
request, response, decision and probe registry make no content protection
assumptions. What gets checked is defined by probe types. Content
protection is the first one, with decode, display and clock probes alongside
it. If a non-DRM use emerges later (for example platform attestation for clear
content), the mechanism can move into MSF without changing its shape.

Why validate the client before media access

Today an MSF session runs in this order: catalog, token, SUBSCRIBE, media,
license. The first check that inspects the client itself, such as its DRM
security level, happens at license acquisition. By then the client has already
learned every track name, been authorized by the relay, and downloaded the
encrypted media. That ordering has real security costs:

  1. One gate instead of two. The license server is the only place where the
    client's security level is enforced. A single key-policy mistake, such as one
    key shared across renditions or across audio and video, exposes premium
    content to any client that holds a token. Preflight adds an independent gate
    at the relay, driven by the same decision as the license.
  2. Ciphertext goes to unverified clients. Any token holder can SUBSCRIBE or
    bulk FETCH encrypted 4K groups whether or not it could ever be licensed for
    them. The more widely encrypted media is distributed, the more any later key
    compromise is worth, and high-bitrate tracks cost relay and CDN egress.
    Preflight limits track access to clients that have met the requirements.
  3. Client claims are easy to fake. Browser-reported capabilities (EME
    robustness, HDCP status, decode support) can be altered by page script.
    Preflight lets the publisher check verifiable evidence, such as a license
    request signed by the client's CDM with a fresh nonce embedded, before any
    token is issued. Spoofed claims and replayed challenges are caught before
    the client touches media.
  4. Track access and DRM entitlement drift apart. When track authorization
    and license policy are decided separately, a client can be authorized for a
    rendition it will be refused a key for. ABR selects it and playback stalls.
    Preflight derives both from one decision, so the catalog, the relay scope
    and the license policy agree.
  5. Least privilege. Authorizing per track based on a verified client
    posture is the same principle as device-posture checks in zero-trust
    access: the client gets exactly what it can safely and legitimately use,
    and nothing it would have to be refused later.

Preflight does not replace DRM or the authorization scheme. Both still enforce.
It makes sure they are fed the same, verified view of the client before media
access begins.

What this enables

  • The relay can enforce a token scoped to exactly the authorized tracks.
  • The catalog the player receives lists only tracks it can use, so ABR doesn't
    select a rendition that will fail at license time.
  • Capability probes are extensible through a registry, so content protection
    specs (CMSF) define their own verifiable probes without MSF needing to know
    about DRM.

Diagrams

Where the client is checked

flowchart LR
  subgraph today["Today"]
    direction LR
    A1[catalog] --> A2[token] --> A3[SUBSCRIBE] --> A4[media] --> A5["license<br/>(first client check)"]
  end
  subgraph pre["With preflight"]
    direction LR
    B1[catalog] --> B2["preflight<br/>(verified client check)"] --> B3[scoped token] --> B4["SUBSCRIBE<br/>(relay enforces scope)"] --> B5[media] --> B6["license<br/>(second, independent check)"]
  end
Loading

Preflight exchange, track access and license

sequenceDiagram
  participant S as Subscriber
  participant R as Relay
  participant P as Preflight service
  participant L as License server
  S->>R: SUBSCRIBE catalog
  R-->>S: catalog with preflight field (no gated tracks usable yet)
  S->>R: SUBSCRIBE request track
  P-->>S: preflight request (nonce, probes)
  Note over S: run probes (decode, display, DRM canary)
  S->>P: preflight response (results, signed CDM challenge with nonce)
  Note over P: verify evidence, decide tier
  P-->>S: decision (authorizedTracks, deniedTracks, scoped token)
  S->>R: SUBSCRIBE authorized track + token
  Note over R: token scope check
  R-->>S: media objects (encrypted)
  S->>L: license request (signed by CDM) + token
  Note over L: verify security level again
  L-->>S: keys for authorized tracks only
Loading

Spoofed client is stopped before media access

An L3-only client claims hardware DRM and replays an L1 challenge captured
from another device.

sequenceDiagram
  participant S as L3 client (spoofing)
  participant R as Relay
  participant P as Preflight service
  participant L as License server
  P-->>S: preflight request (nonce N2)
  S->>P: response: claims HW_SECURE_ALL, replayed L1 challenge (nonce N1) + real L3 challenge (N2)
  Note over P: N1 != N2: replay. Verified level = L3. Flag claim mismatch.
  P-->>S: decision: L3 tracks only, 4K denied, token scoped to L3 tracks
  S->>R: SUBSCRIBE video-2160 (4K)
  R-->>S: error: insufficient scope (no retry)
  S->>R: FETCH past 4K groups
  R-->>S: error: insufficient scope
  S->>L: license request for 4K key (L3 CDM)
  L-->>S: 4K key denied, L3 keys only
Loading

Design

  1. The root catalog gains a preflight field naming a request track (an event
    timeline) and either an HTTPS submitUri or a submitTrack publish track.
  2. Tracks (or referenced catalogs) that need it carry preflightRequired: true.
    Subscribers MUST NOT SUBSCRIBE or FETCH them before a decision.
  3. The request carries a nonce, an expires time, and a probes object
    keyed by probe type.
  4. The response returns one result per probe type, or {"unsupported": true}.
  5. The decision lists authorizedTracks, optional deniedTracks with reasons,
    the catalog to use, and the authorization tokens keyed by the existing
    MSF authInfo scheme identifiers. Out-of-scope requests fail with the
    existing insufficient-scope handling.
  6. A fallbackCatalog gives subscribers that don't support preflight, or whose
    preflight fails, something to play.
  7. The content protection probe sends canary keys whose init data embeds the
    nonce in an integrity-protected field (Widevine PSSH content_id, PlayReady
    CUSTOMATTRIBUTES, FairPlay content identifier). The subscriber returns the
    CDM-signed license request, so the publisher verifies the security level
    itself instead of trusting reported robustness.

Preflight results are explicitly advisory. Enforcement stays with the
authorization scheme and the content protection system.

Changes to draft-ietf-moq-cmsf.md

Location Change
DRM System fields New optional outputProtection field (minimum HDCP version, informative)
New section "Capability Preflight" Catalog fields (preflight at root, preflightRequired on tracks), exchange, request, response, decision, token acquisition via preflight, two ASCII figures (check ordering, exchange sequence)
"Preflight probe types" Generic rules for probe types, then: content protection probe (org.ietf.moq.cmsf.contentprotection: canary keys, robustnessLadder, HDCP, schemes, canary playback, session types, nonce binding figure, informative robustness table), decode, display and clock probes
"Preflight example" Catalog, request, response and decision
Security Considerations Capability preflight (verifiable evidence, nonce replay, token handling), content protection preflight (key separation per level and audio vs video), preflight privacy
IANA Considerations Registers org.ietf.moq.cmsf.preflight.request / .response in the "MSF Event Timeline Types" registry. Creates the "CMSF Preflight Probe Types" registry (Specification Required) with four initial entries
References Adds JSON (RFC 8259) and HTTP (RFC 9110)

Not in this PR

  • Device-environment signals (automation, virtual GPU detection). These are
    deliberately left to application-defined probe types because of their
    fingerprinting impact.

Dependencies

  • None on MSF changes. The draft uses MSF event timeline tracks, publish
    tracks, variable substitution and authorization as they exist today. The
    normative MSF reference is still draft-ietf-moq-msf-01 and may need bumping
    to a version that contains those sections.

Open questions for the WG

  1. Is an HTTPS submitUri acceptable, or should the exchange be MoQT-only
    (submitTrack)? The HTTPS path is included because current web players
    can't yet publish on publishTracks, and it fits existing API gateways.
  2. Should the decision tokens be carried here, or should the decision only
    reference a token endpoint defined by the authorization scheme (C4M,
    Privacy Pass)?
  3. Should the probe registry sit in the "MSF Parameters" registry group (as
    proposed), so it can be shared if preflight later moves to MSF, or in a
    CMSF-specific group? Or should probe types share the event timeline types
    registry?
  4. Should preflightRequired instead be expressed inside authInfo?
  5. Should relays treat request and decision tracks specially (no caching), or
    is a per-session namespace sufficient?
  6. Is a canary key with the nonce in integrity-protected init data an
    acceptable, DRM-neutral way to bind evidence, or should each DRM system's
    binding be specified separately?
  7. Should outputProtection be normative for subscribers (e.g. MUST NOT select
    a track whose HDCP requirement is not met), or stay informative?
  8. Should the robustness value table stay in the draft (informative), or move
    to a registry or an external reference such as DASH-IF?
  9. Is the key-separation guidance (per security level, audio vs video) in
    scope for CMSF, or better left to DASH-IF ECCP?

Checklist

  • Discussed on moq@ietf.org or in a linked issue
  • make builds without new warnings
  • Cross-references resolve ({{preflight}}, {{preflightrequired}},
    {{preflightworkflow}}, {{cppreflight}}, {{outputprotection}},
    {{iana-preflight-probe-types}})
  • Contribution made under the IETF Note Well (BCP 78/79)

Defines the org.ietf.moq.cmsf.contentprotection probe type for the MSF
preflight (moq-wg/msf#214): canary keys with the preflight nonce bound into
integrity-protected init data, result values including signed CDM
challenges, an outputProtection field for minimum HDCP, security guidance,
and the probe type registration.
Per review on moq-wg/msf#214, define the preflight exchange in CMSF
instead of MSF. The mechanism (catalog fields, request, response,
decision, token acquisition, probe registry) is written without DRM
assumptions. Content protection is the first probe type, alongside
decode, display and clock probes. Identifiers move to the
org.ietf.moq.cmsf namespace and the probe registry becomes the
"CMSF Preflight Probe Types" registry.
@deisenbacher deisenbacher changed the title Add content protection probe for MSF capability preflight Add capability preflight, with content protection as the first probe type Oct 30, 2026
@deisenbacher
deisenbacher force-pushed the preflight-contentprotection branch from 30afc0a to 46bea6f Compare October 30, 2026 14:14

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant