Skip to content

Playback

Video protection is the heart of the app. The full story is the ten-part playback deep dive; this is the summary you need before touching player code.

Content is HLS AES-128 — not Widevine/FairPlay DRM (both exist in kvideo but are never configured). Every .ts segment is encrypted with a 16-byte AES key that sits behind a URL at kms.chorki.net, and that URL only answers to three HMAC-signed HTTP headers. Every compliant player already knows how to fetch the key and decrypt — so the app’s entire job is making sure those headers are attached to the request. On the phone that’s trivial; offline it’s replayed from disk; on a Chromecast it’s genuinely hard, because a receiver can send no custom headers at all — and that single fact is why the app runs a local web server.

EncryptionHelper.playerHeader() (lib/core/helper/encryption_helper.dart) builds them:

Header What it is
referer Signed referer
vpsid HMAC-signed viewer/session id
platform mobile
x-entitlement / x-content-access KMS tokens, when token headers are enabled

The design evolved through several iterations (deep-dive docs 9–10 tell the history). The shipped behaviour is cache-first:

  • x-entitlement is a JWT, cached and replayed while its exp is valid (lib/domain/entities/entitlement_token.dart, the entitlement repository
    • use cases).
  • Refreshed shortly before exp; forced refresh on purchase, KMS rejection and account change.
  • x-content-access similarly guards the key file itself.
  • Headers are written at load time, not up front — the stale-token class of bugs (deep dive 8).
Path How the key arrives Key files
Online Player fetches it with the signed headers player_media_builder.dart, video_player_screen.dart
Offline Fetched once at download time, stored encrypted on disk, replayed by the in-app local server lib/data/services/hls/, local_server.dart
Cast / AirPlay The receiver can’t send headers → a LAN proxy inside the app signs key requests on its behalf and rewrites playlists cast_lan_proxy_server_impl.dart, cast_playlist_rewriter.dart

What this protects against and what it doesn’t is written down honestly in Threat model and gaps — read it before proposing changes to the security model. Key-extraction attack paths are analysed in deep dive 11.