Suggestions

close search

Add Messaging, Voice, and Authentication to your apps with Vonage Communications APIs

Visit the Vonage API Developer Portal

End-to-End Encryption

Use the End-to-End Encryption API to encrypt media being sent through a media server from your application.

Important: In OpenTok.js 2.27.0, end-to-end encryption will not work with clients using earlier version of OpenTok.js. When you upgrade your app to use OpenTok.js 2.27.0+, make sure all clients are using OpenTok.js 2.27.0+ if the app uses end-to-end encryption..

This topic includes the following sections:

Overview

End-to-end encryption (or E2EE) allows application developers to encrypt media in routed sessions from client to client. For relayed sessions, media is already encrypted client-to-client, through WebRTC protocols. This feature adds an encryption layer by encrypting the media payload at the client so that it will remain encrypted through the OpenTok Media Router that routes media to other clients (in routed sessions). You enable end-to-end encryption when you create a session.

End-to-end encryption is supported in web apps on Chromium-based browsers (Chrome,Opera, and Edge).

You set the encryption secret in a web application using OpenTok.js. You must set encryption secrets in the web client when you initialize a session, and you can change encryption secrets on the fly once the session has connected. The encryption secret is a non-empty string. All users must use the same secret to receive intelligible media. The encryption secret is key material for generating a cryptographic key that is used to encrypt and decrypt media. Specifically, the encryption secret generates an AES-256 encryption key using an AES-CTR algorithm with a 256-bit key.

Please note that the OpenTok Media Router does not have access to unencrypted media when using end-to-end encryption. So, features that require media decoding — such as archiving, live streaming broadcasts, experience composer, audio connector, and SIP interconnect — are unsupported in end-to-end encrypted sessions.

Adding end-to-end encryption for your account

End-to-end encryption is available as an add-on feature. You can enable it on your Vonage Video account page.

Enabling encryption using the REST API

You enable end-to-end encryption when you create a session using the REST API. Set the e2ee property to true. See session creation.

Note: Before enabling end-to-end encryption for a session, you must enable it for your Vonage Video account. See the previous section.

The following Node.js example creates an end-to-end encryption enabled session:

const opentok = new OpenTok(API_KEY, API_SECRET);
const sessionId;
opentok.createSession({
    mediaMode: 'routed',
    e2ee: true,
  }, function(error, session) {
  if (error) {
    console.log('Error creating session:', error)
  } else {
    sessionId = session.sessionId;
    console.log('Session ID: ' + sessionId);
  }
});

Initializing a session with a secret

Important: In OpenTok.js 2.27.0, end-to-end encryption will not work with clients using earlier version of OpenTok.js. When you upgrade your app to use OpenTok.js 2.27.0+, make sure all clients are using OpenTok.js 2.27.0+ if the app uses end-to-end encryption..

End-to-end encrypted sessions are created using server APIs. To have a web client join an end-to-end encrypted session, specify an encryption secret when calling the OT.initSession() method:

const session = OT.initSession(
  'api-key',
  'session-id',
  {
    encryptionSecret: 'initialEncryptionSecret'
  }
});

A valid secret is a string between 8 and 256 characters. The secret can later be changed with the Session.setEncryptionSecret() method (see "Changing the secret", below)

Checking whether the browser supports end-to-end encryption

Use the OT.hasEndToEndEncryptionSupport() method to check if the client's browser supports end-to-end encryption:

if (OT.hasEndToEndEncryptionSupport()) {
  // Proceed with connecting to the session
}
else {
  // Notify the user that they cannot join the session
}

Currently, end-to-end encryption is only supported in Chrome (desktop and mobile), Opera, and Edge. It is not supported in Firefox or Safari. Also, end-to-end encryption is only supported when using the VP8 codec (the default).

Changing the secret

You can change the secret using the Session.setEncryptionSecret() method after the session has connected:

await session.setEncryptionSecret('newEncryptionSecret');

Events and errors

Events and errors are essential to managing the behavior of user-driven encryption behavior. Everyone in the session is expected to use the same secret to encrypt their media and decrypt everyone else's. A Subscriber object dispatches an encryptionSecretMismatch event when the subscriber is unable to decode a stream's media. It is important to communicate to the user that media is not being received due to an encryption mismatch and not due to a connection failure or audio/video bug:

subscriber.on('encryptionSecretMismatch', () => {
  // Activate a UI element communicating that there's been an encryption secret mismatch.
});

Also, it is important to communicate to users that encryption has been successfully enabled. A Subscriber object dispatches an encryptionSecretMatch event when the subscriber is able to decode the stream's media after a previous mismatch.

subscriber.on('encryptionSecretMatch', () => {
  // Activate a UI element communicating that the media is being properly decrypted.
});

The Session.connect() callback is invoked with an error if the client tries to connect to an end-to-end encrypted session that was initialized with an invalid encryption secret. A valid secret is a string between 8 and 256 characters. For the best user experience, the application should catch an invalid user supplied secret before calling theOT.initSession() method. In the following example, a session is initialized with an empty (and thus invalid) secret, which causes an error when attempting to connect:

const session = OT.initSession(
 'api-key',
 'e2ee-session-id',
 {
   encryptionSecret: '',
 }
)

session.connect('token', (error) => {
  if (error && error.name === 'OT_INVALID_ENCRYPTION_SECRET') {
    /*
    The error will have the message `encryption secret used is malformed`.
    The application should communicate that the secret was invalid.
    */
  }
});

The Session.connect() callback is invoked with an error if a user attempts to connect to an end-to-end encrypted session in a browser that does not support end-to-end encryption.

const session = OT.initSession(
 'api-key',
 'e2ee-session-id',
 {
   encryptionSecret: 'validEncryptionSecret',
 }
)

session.connect('token', (error) => {
  if (error && error.name === 'OT_UNSUPPORTED_BROWSER') {
    /*
    The error will have the message 'Tried to connect an e2ee session but the browser does not support e2ee'
    The application should communicate that the browser does not support encryption.
    */
  }
});

If a user tries to publish in an end-to-end encrypted session without having specified an encryption secret, the Session.publish() callback is invoked with an error. For the best user experience, the application should validate an user-supplied secret before calling the session.publish() method:

session.publish(publisher, (error) => {
  if (error && error.name === 'OT_STREAM_CREATE_FAILED') {
    /*
    The error will have the message 'Tried to publish to an e2ee session but encryption secret was not set'
    The application should communicate that the secret was not set.
    */
  }
});

If a user tries to subscribe in an E2EE session without having specified an encryption secret, the Session.subscribe() callback is invoked with an error. For the best user experience, the application should validate a user-supplied secret before calling the Session.subscribe() method:

subscriber = session.subscribe(stream, target, opts, (error) => {
  if (error && error.name === 'OT_UNABLE_TO_SUBSCRIBE') {
    /*
    The error will have the message 'Tried to subscribe to an e2ee stream but encryption secret was not set'
    The application should communicate that the secret was not set.
    */
  }
});