> ## Documentation Index
> Fetch the complete documentation index at: https://docs.livepeer.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Upload encrypted assets

> Learn how to upload and play back an encrypted video asset

Livepeer enables you to upload encrypted video content or import it from
decentralized storages. It creates a playback URL that can have independent
access control through Livepeer's
[access control](/developers/guides/overview#access-control) feature.

<Info>
  This guide is written for developers using JavaScript, but the concepts can be
  applied to any language.
</Info>

### Generate an encryption key

First, we generate a 256-bit encryption key to encrypt the file.

```js theme={"theme":{"light":"github-light","dark":"dark-plus"}}
// Generate a random 256-bit key
const key = await window.crypto.subtle.generateKey(
  {
    name: "AES-CBC",
    length: 256,
  },
  true,
  ["encrypt", "decrypt"]
);

// Export the key as raw data
const keyData = await window.crypto.subtle.exportKey("raw", key);

// Encode the key in Base64
const keyBase64 = btoa(String.fromCharCode(...new Uint8Array(keyData)));
```

### Encrypt your video

To encrypt a video file with the key we just generated, we follow these steps:

1. Generate a random 128-bit initialization vector (IV).
2. Pad the data using PKCS#7 padding.
3. Encrypt the data using AES-CBC.

```js theme={"theme":{"light":"github-light","dark":"dark-plus"}}
const iv = window.crypto.getRandomValues(new Uint8Array(16));

const encrypted = await window.crypto.subtle.encrypt(
  {
    name: "AES-CBC",
    iv: iv,
  },
  key, // from generateKey or importKey above
  arrayBuffer // ArrayBuffer of data you want to encrypt
);

// Concatenate IV and encrypted file into a new ArrayBuffer
const resultBuffer = new ArrayBuffer(iv.length + encrypted.byteLength);
new Uint8Array(resultBuffer).set(new Uint8Array(iv), 0);
new Uint8Array(resultBuffer).set(new Uint8Array(encrypted), iv.length);

const blob = new Blob([resultBuffer], { type: "application/octet-stream" });
```

<Info>
  Currently Livepeer supports only video content encrypted using
  [SubtleCrypto.encrypt](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/encrypt)
  with `AES-CBC` algorithm, which is also the encryption used by other web3
  protocols like
  [Lit](https://developer.litprotocol.com/sdk/explanation/encryption/). It can
  implemented in other environments with regular AES-CBC encryption using PKCS#7
  padding.
</Info>

### Retrieve the Livepeer Public Key

After obtaining an encryption key and encrypting a video file with it, the next
step is to retrieve the Livepeer Public Key. This key will be used to encrypt
our encryption key, which can then be shared with Livepeer when creating a video
asset.

```js theme={"theme":{"light":"github-light","dark":"dark-plus"}}
// Fetch the public key from Livepeer
const publicKeyResponse = await fetch(
  "https://livepeer.studio/api/access-control/public-key",
  {
    headers: {
      Authorization: "Bearer XXXX-XXXXXX-XXXXXXX-XXXX",
    },
  }
);

const publicKeyData = await publicKeyResponse.json();
const publicKey = publicKeyData.spki_public_key;
```

### Encrypt the encryption key

Now, we can use asymmetric encryption with the Livepeer Public Key to encrypt
our Encryption Key. The resulting data is then encoded in base64 before being
sent to Livepeer along with the video file.

<Info>
  The Web Cryptography API built into modern browsers does not directly support
  PEM or PKCS#1 formatted keys. It only supports the SPKI format for public keys
  and the PKCS8 format for private keys. In Javascript, you need to use the
  `spki_public_key`.
</Info>

```js theme={"theme":{"light":"github-light","dark":"dark-plus"}}
// Decode the SPKI public key from base64 and convert it to a buffer
const spkiPublicKey = atob(publicKeyData.spki_public_key);
const publicKeyBuffer = Uint8Array.from(atob(spkiPublicKey), (c) =>
  c.charCodeAt(0)
).buffer;

// Import the public key
const publicKey = await window.crypto.subtle.importKey(
  "spki",
  publicKeyBuffer,
  {
    name: "RSA-OAEP",
    hash: { name: "SHA-256" },
  },
  false,
  ["encrypt"]
);

// Encrypt the key data with the public key
const encryptedKeyData = await window.crypto.subtle.encrypt(
  {
    name: "RSA-OAEP",
  },
  publicKey,
  keyData
);

// Base64 encode the encrypted key data
const encryptedKeyBase64 = btoa(
  String.fromCharCode(...new Uint8Array(encryptedKeyData))
);
```

### Upload/import the video file

When requesting an upload to the Livepeer API, you can simply
[add an `encryption` field](/api-reference/asset/upload-via-url) with your
encrypted key.

<Warning>
  When uploading an encrypted video, specifying a `playbackPolicy` is required.
  You can still specify the type of the playback policy as `public`, but be
  aware that anyone with the `playbackId` or `playbackUrl` would be able to
  watch it.
</Warning>

```js theme={"theme":{"light":"github-light","dark":"dark-plus"}}
// Request the upload URL
const response = await fetch(
  "https://livepeer.studio/api/asset/request-upload",
  {
    method: "POST",
    headers: {
      Authorization: "Bearer XXXX-XXXXXX-XXXXXX-XXXX",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      name: "File name",
      encryption: {
        encryptedKey: encryptedKeyBase64,
      },
      playbackPolicy: {
        // Your playback policy
      },
    }),
  }
);

if (!response.ok) {
  alert("Error requesting upload URL");
  return;
}

const data = await response.json();

// Upload the encrypted file to the returned URL
const uploadResponse = await fetch(data.url, {
  method: "PUT",
  body: encryptedFile,
});
```
