Skip to main content
Version: 5.x.x

End-to-End Encryption (E2EE) - Flutter

Overview​

End-to-end encryption (E2EE) ensures that your media content remains private and secure by encrypting it on the sender's device and decrypting it only on the receiver's device. This prevents intermediaries, including VideoSDK servers, from accessing or modifying the content.

E2EE is particularly important for applications in security-critical domains such as telehealth, finance, and legal services.

E2EE Modes​

VideoSDK offers two modes of End-to-End Encryption (E2EE) for securing media streams during sessions:

  1. Shared Key (Room-Level): A single encryption key is used across the entire session, encrypting and decrypting media tracks for all participants.
  2. Per-Participant Keys: Individual participants have unique encryption keys, providing enhanced security by ensuring each participant's media streams are encrypted separately.

How E2EE Works​

  • E2EE is applied at the session level, where in Shared Key mode, a single key encrypts and decrypts all media tracks for every participant, while in Per-Participant Keys mode, each participant is assigned a unique key to encrypt their individual media streams separately.
  • Participants without the key cannot access the media.
  • The entire encryption process is handled client-side. VideoSDK does not access or store any encryption keys.

Key Management & Distribution​

You are fully responsible for generating, managing, and securely distributing encryption keys to all participants. VideoSDK never stores, accesses, or transfers your encryption keys.

  • Common Key Distribution Approaches:
    • Generate the key on your server when creating a room.
    • Send the key securely along with the room access token.
    • Use HTTPS-secured API calls to fetch the key on the client.
important

⚠️ Ensure that all participants have access to the correct encryption key before joining the room.

SDK Version Compatibility​

The table below lists the minimum SDK versions that support E2EE.

FlutteriOSReact NativeReactAndroidJavaScript
2.1.02.2.20.2.10.3.50.5.00.2.6

Enabling E2EE in VideoSDK Flutter​

VideoSDK provides two ways to enable End-to-End Encryption (E2EE):

  1. Shared Key: A single encryption key shared by all participants.
  2. Per-Participant Key: Unique encryption keys assigned to individual participants.

Encryption is turned on when you join the room, so set your keys and call VideoSDK.setKeyProvider() before you call join(). If no key is set when you join, keys you set later do not encrypt that room.

1. Enabling E2EE using a Shared Key​

When using a shared key, all participants utilize the same encryption key, set via the setSharedKey() method.

@override
void initState() {
super.initState();

// Set up the encryption key, then create and join the room
unawaited(setupRoom()); // unawaited() is in dart:async
}

Future<void> setupRoom() async {
// Set up the encryption key
try {
await keySetup();
} catch (e) {
print("Error setting encryption key: ${e.toString()}");
return;
}

// Initialize the room instance
Room room = VideoSDK.createRoom(
roomId: roomId,
token: token,
participantName: displayName,
);

try {
await room.join();
} on VideoSDKError catch (e) {
print("Error joining the room: ${e.code} ${e.name}");
}
}

Future<void> keySetup() async {
var baseKeyProvider = BaseKeyProvider();

// Set a single shared encryption key for all participants
await baseKeyProvider.setSharedKey('<Your Encryption Key>');

// Set the key provider for E2EE
VideoSDK.setKeyProvider(baseKeyProvider);
}
warning

Wait for setSharedKey() to complete before you join the room: the shared key is read when you join. It throws an ArgumentError for an empty key.

2. Enabling E2EE using Per-Participant Keys​

Per-Participant Keys enhance security by assigning a distinct encryption key to each participant, ensuring that access is uniquely controlled on a per-user basis.

  • Every participant needs the keys of all participants, including its own. The media of a participant who has no key is not encrypted.
  • Participant keys are used only when no shared key is set.
Step 1. Initialize the BaseKeyProvider and Set Up Encryption Keys​
  • Install a BaseKeyProvider with VideoSDK.setKeyProvider(), then set the localParticipantId along with its corresponding key using VideoSDK.setParticipantKey().
  • Store each participant's ID and key securely on your server for decryption purposes.
class RoomScreen extends StatefulWidget {
const RoomScreen({super.key});

@override
State<RoomScreen> createState() => _RoomScreenState();
}

class _RoomScreenState extends State<RoomScreen> {

@override
void initState() {
super.initState();

String localParticipantId = "YOUR_LOCAL_PARTICIPANT_ID";

// Set up the encryption key, then create and join the room
unawaited(setupRoom(localParticipantId, "SECRET_KEY")); // unawaited() is in dart:async
}

Future<void> setupRoom(String localParticipantId, String key) async {
// Set up the encryption key
if (!await keySetup(localParticipantId, key)) return;

// Initialize the room instance
Room room = VideoSDK.createRoom(
roomId: roomId,
token: token,
participantName: displayName,
participantId: localParticipantId,
);

try {
await room.join();
} on VideoSDKError catch (e) {
log("Error joining the room: ${e.code} ${e.name}");
}
}

Future<bool> keySetup(String participantId , String key) async {
try {
VideoSDK.setKeyProvider(BaseKeyProvider());
// Per-Participant E2EE: Set key for local participant
await VideoSDK.setParticipantKey(participantId, key); // Your Encryption Key
return true;
} catch (e) {
log("Error in setting the keys: ${e.toString()}");
return false;
}
}
}
warning

Wait for setParticipantKey() to complete before you join the room. It throws an ArgumentError for an empty participant ID or key.

Step 2. Decrypting Participant Streams using Per-Participant Keys​
  • When a participant joins, retrieve the corresponding encryption key (previously stored on your server) using the participant’s ID to decrypt their media streams.
  • Call VideoSDK.setParticipantKey() with that remote participant's ID and key to decrypt their media stream.
  • Call registerRoomEvents() with the room from Step 1 before you call join().
class _RoomScreenState extends State<RoomScreen> {

void registerRoomEvents(Room room) {

room.on(Events.participantJoined, (Participant participant) async {
try {
// Retrieve key from your server using participant.id
String key = "SECRET_KEY";

// Set the retrieved key to decrypt incoming participant streams
await VideoSDK.setParticipantKey(participant.id, key);
} catch (e) {
log("Error in setting the keys: ${e.toString()}");
}
});
}
}
important
  • Ensure all encryption keys are securely stored and retrieved from your server.
  • Proper key management is essential for effective encryption and decryption, especially when using Per-Participant Keys.
  • A participant's media is decrypted only if their key is set before their media reaches you. Set a new participant's key as soon as they join, and set the keys of participants who are already in the room before you join.

Removing keys and turning E2EE off​

  • To remove one participant's key, pass null as the key: await VideoSDK.setParticipantKey(participantId, null);. You can also call removeKey(participantId) on the key provider you installed. Their media stops being decrypted at once.
  • To turn E2EE off, call VideoSDK.removeKeyProvider() before you join.
  • VideoSDK.getKeyProvider() returns the key provider in use, or null when E2EE is off.

To check whether E2EE is enabled​

You can check whether E2EE is enabled by using e2eeEnabled property of Room class. It is true once a key provider that holds at least one key is set with VideoSDK.setKeyProvider().

bool isE2EEEnabled = _room.e2eeEnabled;
print("Is E2EE Enabled: $isE2EEEnabled");

Limitations​

E2EE only applies to media. It does not apply to:

  • Chat messages or metadata
  • API calls and signaling data

These communications are still protected by TLS but are not encrypted end-to-end.

info

Recording and transcription features are not supported when End-to-End Encryption (E2EE) is enabled.

API Reference​

The API references for all the methods utilized in this guide are provided below.

Got a Question? Ask us on discord