Encrypted playback, from first principles
Encrypted playback, from first principles
Section titled “Encrypted playback, from first principles”A six-part primer on how this app’s video is protected, how the key that unlocks it is obtained, and how three different playback paths each solve that problem differently. Written for someone who has never worked with encrypted streaming — each doc builds on the last, and each one ties the general concept back to the real files in this codebase.
Read them in order the first time through:
- AES-128 and the KMS — what “encrypted video” actually means here, why it is not DRM, where the key lives, and the long obfuscated journey the credentials take before they can ask for it.
- Online Playback — the whole path from a tap on a poster to a decrypted frame on screen, through Flutter, a Pigeon bridge, and finally ExoPlayer or AVPlayer.
- Offline Downloads — what actually lands on disk when you download a film, why a tiny web server runs inside the app to play it back, and why the key file needs its own MIME type.
- Cast and AirPlay — why a TV cannot fetch
the key on its own, and the LAN proxy that signs the request on its
behalf. The short version of
../casting/04and../casting/06. - Threat Model and Gaps — what this
design protects against, what it does not, and what is already fixed
on a branch but not on
main. - The New KMS Authorization — an
assessment of the incoming
x-entitlement/x-content-accessscheme: which of those gaps it closes, which it does not, and what it would cost this app to adopt. Proposed; not implemented. - KMS Token Authorization — Implementation Plan — the shipped client-side plan for the scheme doc 6 assessed: where each token is fetched, cached and forwarded, and how each playback path was taught to carry it.
- The Stale KMS Token Fix — headers written at load time instead of up front, the per-title token service, and the one-shot retry after a playback error.
- A Fresh Entitlement Token on Every Play
—
PlaybackAuthResolver: every play fetchesx-entitlementfrom the server, falling back to the cached one if that fails. Superseded by doc 10. - Entitlement Token: Cache First, Refresh on Expiry
— the current design: the cached token is used while its JWT is valid,
refreshed shortly before
exp, and forced on purchase, KMS rejection and account change. Domain repository + use cases.
The one-paragraph version
Section titled “The one-paragraph version”The video is not DRM-protected. Every .ts segment is encrypted with
plain AES-128, a scheme built into the HLS spec itself, and the
16-byte key sits behind a URL that only answers to three HMAC-signed HTTP
headers. Every compliant player already knows how to fetch that key and
decrypt — so the app’s entire job is to make sure those three headers are
attached to the request. On the phone that is trivial. Offline, the key is
fetched once at download time and replayed from disk. On a Chromecast or
Apple TV it is genuinely hard, because a receiver can send no custom
headers at all, and that single fact is why this app runs a web server.
Which state this describes
Section titled “Which state this describes”main, as of August 2026. Where a fix exists on
feature/anti-piracy-hardening but is not merged, doc 5 says so
explicitly — nothing in docs 1–4 describes unmerged code. Doc 6 is the
one forward-looking chapter: it describes a backend change that has not
shipped, and is marked as such at the top.
Map to the code
Section titled “Map to the code”| Doc | Primary files |
|---|---|
| 1. AES-128 & the KMS | lib/core/helper/encryption_helper.dart, lib/data/models/settings_model.dart, lib/data/datasources/settings_data_source.dart |
| 2. Online Playback | lib/presentation/widgets/player/player_media_builder.dart, lib/presentation/pages/content_pages/video_player_screen.dart, lib/presentation/widgets/player/cast/cast_aware_navigation.dart, plus ottstack_player / kvideo (path deps outside this repo) |
| 3. Offline Downloads | lib/data/services/hls/ (parser, task factory, orchestrator, playlist writer), lib/domain/services/local_server.dart, lib/data/services/local_playback_server_controller_impl.dart |
| 4. Cast & AirPlay | lib/data/services/cast/cast_lan_proxy_server_impl.dart, cast_playlist_rewriter.dart, lib/presentation/widgets/player/airplay/airplay_controller.dart |
| 5. Threat Model | all of the above, plus lib/core/security/key_file_crypto.dart on feature/anti-piracy-hardening |
| 6. New KMS Auth | nothing yet — would touch encryption_helper.dart, player_media_builder.dart, cast_lan_proxy_server_impl.dart, download_orchestrator.dart, user_auth_data_source.dart |
| 7. Implementation | lib/data/services/entitlement_service.dart, lib/data/datasources/entitlement_data_source.dart, lib/core/helper/encryption_helper.dart, lib/data/services/cast/cast_lan_proxy_server_impl.dart, lib/domain/entities/cache_content_entity.dart |
| 8. Stale Token Fix | lib/data/services/content_access_token_service.dart, lib/core/helper/token_expiry.dart, lib/data/services/entitlement_service.dart, lib/presentation/widgets/player/player_media_builder.dart, lib/presentation/pages/content_pages/video_player_screen.dart |
| 9. Entitlement per Play | lib/domain/services/playback_auth_resolver.dart, lib/data/services/playback_auth_resolver_impl.dart, lib/presentation/widgets/player/player_media_builder.dart, lib/presentation/widgets/player/cast/cast_aware_navigation.dart, lib/presentation/widgets/player/cast_controller.dart, lib/data/services/cast/cast_lan_proxy_server_impl.dart, lib/data/services/hls/download_orchestrator.dart |
| 10. Cache-first Entitlement | lib/domain/entities/entitlement_token.dart, lib/domain/repositories/entitlement_repository.dart, lib/data/repositories/entitlement_repository_impl.dart, lib/data/datasources/local/entitlement_token_store.dart, lib/data/services/entitlement_service.dart, lib/data/services/playback_auth_resolver_impl.dart |
| 12. Playback Ownership | lib/presentation/widgets/player/chorki_player_controller.dart (PlaybackLease), playback_route_observer.dart, media_copy.dart, lib/presentation/pages/content_pages/single_content_screen.dart, ottstack lib/controller/os_player_controller.dart |
Related reading
Section titled “Related reading”-
12. Playback Ownership — the shared player’s lease API, the slow-network trailer leak behind the Subscribe page, and why ottstack’s load guard is pinned by a contract test.
-
../casting/— the seven-part casting primer. Doc 4 of that series is the deep version of doc 4 here; doc 6 covers AirPlay in full. -
../api/README.md— the REST surface. Note that the KMS does not appear there: it is not an app endpoint.