# End-to-end encryption (Beta) (/en/realtime-media/interactive-live-streaming/build/secure-and-protect-channels/end-to-end-encryption)

> For AI agents: see the complete documentation index at [llms.txt](/llms.txt).

**Supported platform:** Web.


      
To enhance data security, Agora provides media stream encryption and end-to-end encryption. End-to-end encryption encrypts the media stream on the sending device and decrypts it on the receiving device, ensuring that third parties cannot access the data during transmission.

The end-to-end encryption is based on the [WebRTC Encoded Transform](https://www.w3.org/TR/webrtc-encoded-transform/) standard. However, since WebRTC Encoded Transform is still maturing and may have compatibility or performance issues, there are some restrictions on its use:

* Only VP8 video and Opus audio are supported.
* It supports publishing and receiving audio and video streams between Web clients only, with no cross-platform compatibility or bypass streaming services.
* The SEI information transmission feature cannot be enabled due to conflicts with browser components.

## Understand the tech

The end-to-end encryption technology is based on the following key concepts:

* **WebRTC Encoded Transform**: A technology for encoding transformations in WebRTC. It provides a read and write interface for encoded WebRTC data, enabling developers to convert encoding formats, adjust bit rates, or apply filters.
* **WebRTC Encoded Transform processing pipeline**: A sequence of operations used to process WebRTC encoded data.
* **RTCRtpSender and RTCRtpReceiver**: Standard WebRTC components. `RTCRtpSender` handles sending encoded data, while `RTCRtpReceiver` is responsible for receiving it. Together, they form the WebRTC Encoded Transform processing pipeline.
* **RTCRtpTransceiver**: A WebRTC API for managing both audio and video streams. `RTCRtpSender` and `RTCRtpReceiver` are accessible through the `.sender` and `.receiver` properties of the `RTCRtpTransceiver` object.

For each audio or video track, the Web SDK offers the `getRTCRtpTransceiver` method to retrieve the `RTCRtpTransceiver`. After obtaining `RTCRtpSender` and `RTCRtpReceiver` from `RTCRtpTransceiver`, set up the WebRTC Encoded Transform processing pipeline to encrypt and decrypt audio or video data.

## Prerequisites

* Ensure that you have implemented the [SDK quickstart](../../index) in your project.
* A supported browser:
  * Chrome (desktop and mobile): version 87 or later
  * Safari (desktop and mobile): version 15.4 or later
* Contact [support@agora.io](mailto\:support@agora.io) to enable end-to-end encryption.

## Implement end-to-end encryption

This section shows you how to implement video encryption on the sender side and decryption on the receiver side using Chrome as an example. The steps for Safari are slightly different.

The example uses the AES encryption algorithm from the Web Crypto API. To implement a custom encryption and decryption algorithm, use `WebAssembly`.

1. **Publish the encrypted media stream**

   To establish a WebRTC Encoded Transform processing pipeline on the publishing end:

   1. After publishing the local video track, call the `getRTCRtpTransceiver` method provided by the SDK to obtain the local video track's `RTCRtpTransceiver` instance. Then, retrieve the `RTCRtpSender` instance through the `sender` property.

   2. Use the browser's native Streams API to encrypt the video stream.

   Refer to the following sample code:

   ```javascript
   async function publish() {
     // Publish the local video track
     await client.publish([localVideoTrack]);

     // Get the RTCRtpTransceiver instance for the local video track
     const transceiver = localVideoTrack.getRTCRtpTransceiver();
     if (!transceiver || !transceiver.sender) {
       return;
     }
     // Get the RTCRtpSender to control the media data being sent
     const sender = transceiver.sender;

     // Define the encryption method for the Chrome browser
     if (isChrome) {
       // Create an encoded stream for encryption transformation
       const streams = sender.createEncodedStreams();
       // Create the encryption transformer
       const transformer = new TransformStream({
         transform(chunk, controller) {
           // The first 7 bytes of VP8 video key frames and the first 3 bytes of non-key frames need to be preserved and not encrypted; otherwise, media transmission will fail
           const vp8ReservedSize = chunk.type === 'key' ? 7 : 3;
           // Implement the encrypt method yourself according to your encryption requirements
           // The example uses the AES encryption algorithm provided by the Web Crypto API
           const encryptedChunk = encrypt(chunk, key, vp8ReservedSize);
           controller.enqueue(encryptedChunk);
         },
       });

       // Connect the encoded stream and the encryption transformer and write the result to the data stream
       streams.readable.pipeThrough(transformer).pipeTo(streams.writable);
     }
   }
   ```

2. **Subscribe to the encrypted media stream**

   To establish the WebRTC Encoded Transform processing pipeline on the receiving end:

   1. When subscribing to a remote video stream, call the `getRTCRtpTransceiver` method provided by the SDK to obtain an instance of the remote video track's `RTCRtpTransceiver`.

   2. Call the browser's native Streams API to decrypt the video stream.

   Refer to the following sample code:

   ```javascript
   async function subscribe(user, mediaType) {
     // Get the RTCRtpTransceiver instance of the remote video track
     const transceiver = user.videoTrack.getRTCRtpTransceiver();
     if (!transceiver || !transceiver.receiver) {
       return;
     }

     // Get the RTCRtpReceiver to control the received media data
     const receiver = transceiver.receiver;

     // Define the decryption method for Chrome browser
     if (isChrome) {
       // Create an encoded stream for decryption
       const streams = receiver.createEncodedStreams();
       // Create a decryption transformer
       const transformer = new TransformStream({
         transform(chunk, controller) {
           // The first 7 bytes of VP8 video keyframes and the first 3 bytes of non-keyframes do not need decryption
           const vp8ReservedSize = chunk.type === 'key' ? 7 : 3;
           // Implement the decrypt method yourself according to your decryption requirements
           const decryptedChunk = decrypt(chunk, key, vp8ReservedSize);
           controller.enqueue(decryptedChunk);
         },
       });

       // Connect the encoded stream and the decryption transformer, and write the results to the data stream
       streams.readable.pipeThrough(transformer).pipeTo(streams.writable);
     }
   }
   ```

## Reference

This section contains content that completes the information on this page, or points you to documentation that explains other aspects of this product.

### Development considerations

If the SDK disconnects and then reconnects, the `RTCRtpTransceiver` instance corresponding to the current track may change. Obtain the new `RTCRtpTransceiver` object through the following callbacks:

* **Local track**: `transceiver-updated`
* **Remote track**: `transceiver-updated`

After enabling end-to-end encryption, browser restrictions require creating an empty encryption pipeline for local tracks created using the Web SDK, even if encryption isn't needed. Without this, media data cannot be sent. Use the following code to create the empty encryption pipeline:

```javascript
async function publish() {
  // Publish the local video track
  await client.publish([localVideoTrack]);

  // Get the RTCRtpTransceiver instance of the local video track
  const transceiver = localVideoTrack.getRTCRtpTransceiver();
  if (!transceiver || !transceiver.sender) {
    return;
  }
  // Get the RTCRtpSender to control the media being sent
  const sender = transceiver.sender;

  // Define the encryption mechanism for Chrome
  if (isChrome) {
    // Create encoded streams for transformation
    const streams = sender.createEncodedStreams();
    // Create a transform stream for handling the data
    const transformer = new TransformStream({
      transform(chunk, controller) {
        // Directly write back the data
        controller.enqueue(chunk);
      },
    });
    // Pipe the encoded stream through the transformer and write it to the data stream
    streams.readable.pipeThrough(transformer).pipeTo(streams.writable);
  }
}
```

### API reference

* Provided by the Web SDK
  * [`ILocalTrack.getRTCRtpTransceiver`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/ilocalaudiotrack.html#getrtcrtptransceiver)
  * [`IRemoteTrack.getRTCRtpTransceiver`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/iremoteaudiotrack.html#getrtcrtptransceiver)
* Browser Native Streams API
  * [`TransformStream`](https://developer.mozilla.org/en-US/docs/Web/API/TransformStream/TransformStream)
  * [`pipeThrough`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/pipeThrough)
  * [`pipeTo`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/pipeTo)

    
  
