# Encrypt HLS streams

Protect video content with AES-128 encryption in HLS packages

Unprotected HLS segments can be downloaded and redistributed trivially. "Masterwork" (a paid video course platform like MasterClass) needs to encrypt stream segments so only authenticated viewers can decrypt and play them.

## API

<CodeGroup labels={["CLI", "TypeScript", "Python", "curl"]}>
```bash
ittybit adaptive \
  -i https://masterwork-app.com/courses/photography/lesson-1.mov \
  --format hls \
  --encryption aes-128 \
  --key-url https://masterwork-app.com/keys/photography/lesson-1
```

```typescript
const task = {
  input: 'https://masterwork-app.com/courses/photography/lesson-1.mov',
  kind: 'adaptive_video',
  options: {
    format: 'hls',
    encryption: {
      method: 'aes-128',
      key_url: 'https://masterwork-app.com/keys/photography/lesson-1',
    },
  },
};

const res = await fetch('https://api.ittybit.com/jobs', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.ITTYBIT_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(task),
});
const data = await res.json();
```

```python

task = {
    "input": "https://masterwork-app.com/courses/photography/lesson-1.mov",
    "kind": "adaptive_video",
    "options": {
        "format": "hls",
        "encryption": {
            "method": "aes-128",
            "key_url": "https://masterwork-app.com/keys/photography/lesson-1",
        },
    },
}

res = requests.post(
    "https://api.ittybit.com/jobs",
    headers={"Authorization": f"Bearer {api_key}"},
    json=task,
)
data = res.json()
```

```bash
TASK='{
  "input": "https://masterwork-app.com/courses/photography/lesson-1.mov",
  "kind": "adaptive_video",
  "options": {
    "format": "hls",
    "encryption": {
      "method": "aes-128",
      "key_url": "https://masterwork-app.com/keys/photography/lesson-1"
    }
  }
}'

curl -X POST https://api.ittybit.com/jobs \
  -H "Authorization: Bearer $ITTYBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$TASK"
```

</CodeGroup>

Each `.ts` segment is encrypted with AES-128. The manifest includes an `#EXT-X-KEY` tag pointing to `key_url`. The player fetches the key before decrypting segments.

## CLI

```bash
ittybit adaptive \
  -i lesson-1.mov \
  -o lesson-1.m3u8 \
  --encryption aes-128 \
  --key-url https://masterwork-app.com/keys/photography/lesson-1
```

## How AES-128 HLS encryption works

1. Ittybit generates a random 128-bit encryption key
2. Each segment is encrypted with that key using AES-128-CBC
3. The manifest references `key_url` -- the endpoint your server exposes to serve the key
4. The player requests the key, then decrypts segments on the fly

## Securing the key endpoint

The encryption is only as strong as the key delivery. Your `key_url` endpoint must:

| Requirement             | Why                                               |
| ----------------------- | ------------------------------------------------- |
| Require authentication  | Prevents anonymous key retrieval                  |
| Use HTTPS               | Prevents key interception in transit              |
| Check entitlement       | Only serve keys to users who purchased the course |
| Set short cache headers | Limits key exposure window                        |

Example: check the session cookie or JWT, verify the user has access to the course, then return the raw 16-byte key with `Content-Type: application/octet-stream`.

## Encryption methods

| Method     | Browser support     | Protection level                |
| ---------- | ------------------- | ------------------------------- |
| AES-128    | All HLS players     | Good -- prevents casual copying |
| SAMPLE-AES | Safari, some others | Better -- encrypts NAL units    |

AES-128 is the most widely supported option and sufficient for most paid content platforms. For premium content requiring stronger DRM, consider FairPlay (Apple) or Widevine (Google) integrations.