End-to-End Encryption Specifications

Prev Next

End-to-End Encryption (E2EE) is a secure communication method that ensures only message senders and recipients can read the content of messages and files. Rocket.Chat enhances security by supporting E2EE for private and direct conversations.

This document explains the technical implementation of E2E Encryption in Rocket.Chat, including key generation, encryption processes, and how the system manages encrypted data. It is intended to supplement the user guide with a deeper technical context.

Key concepts and components

IMPORTANT: Consider the limitations of E2E Encryption before enabling it:

  • Encrypted messages will not appear in search results

  • Encrypted content cannot be audited

  • Bots may not be able to access encrypted messages unless explicitly supported

Found a bug? Please report it to Rocket.Chat.

Encryption password and master key

When a user logs in for the first time, the client automatically generates a mnemonic recovery phrase and prompts the user to save it securely.

This phrase is used to derive a 256-bit AES-GCM Master Key through a secure key-derivation process. The Master Key encrypts and decrypts the user’s private key, which is essential for securing end-to-end encrypted messages.

Public-private key pair (Ku, Kr)

Each user has a public-private key pair (Ku, Kr) generated by the client upon first login. The public key (Ku) is stored on the server, while the private key (Kr) is first encrypted using the AES-GCM Master Key derived from the user’s mnemonic recovery phrase and then sent to the server for secure storage in the user record.

If a public-private key pair already exists in the database for the user, it is downloaded from the server instead of being generated again. The downloaded public key is used as-is, and the encrypted private key is decrypted locally using the Master Key. If the master key has not yet been unlocked on the client, the user is prompted to enter their recovery phrase to restore access.

Session key (Ks)

The public key encrypts a persistent session key (Ks), which is then used to encrypt messages and files. This encrypted session key is stored in the database in the Subscription model for every user in a room (including the user who initiates the E2EE session).

This method works for direct messages and groups since direct messaging is just a room with only two people in it.

Message encryption and decryption

When starting a new E2EE session, if an existing session key exists in the current user's room subscription, it is downloaded and decrypted using the user’s private key and then used to encrypt future messages. If an existing session key is not found in the database, the current user generates a new one, which is then encrypted for every user in the room and stored in the database.

Once a session key has been obtained above, we enter E2EE mode, and all messages sent henceforth are encrypted using this session key.

Because keys are stored in the database and are persistent, the other users in the room do not need to be online to participate in an E2EE conversation.

Code references

The relevant code for Rocket.Chat’s E2EE implementation is located in the following location:

Algorithms and encryption standards

  • Client key pair: RSA-OAEP, 2048-bit (SHA-256)

  • Master key: AES-GCM, 256-bit, PBKDF2 with 100,000 iterations and random salt

  • Session key: AES-GCM, 256-bit

Encryption process

1. User login and key generation

When users log in, they are prompted for their E2EE password. This password is used to generate the Master Key through a Password-Based Key Derivation Function (PBKDF2). The server then checks whether a public-private key pair already exists for the user:

  • If an existing key pair is found, the private key is downloaded and decrypted using the Master Key.

  • If no key pair exists, a new one is generated.

2. Client startup

During startup, the function startClient() in rocketchat.e2e.ts verifies whether the client’s local storage contains a public-private key pair.

  • If no key pair is found, the client is treated as new, and a new RSA-OAEP key pair is generated using the following call:

crypto.subtle.generateKey({
    name: 'RSA-OAEP',
    modulusLength: 2048,
    publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
    hash: {
        name: 'SHA-256'
    }
}, true, ['encrypt', 'decrypt']);

The generated key pair is stored in the client’s local storage in serialized (JSON-stringified) form. The public key and encrypted private key are also uploaded to the server database through the e2e.setUserPublicAndPrivateKeys API method.

3. Starting an E2EE session

When a client joins a room, the handshake() function in rocketchat.e2e.room.js initiates the session setup. The process begins by checking the database for an encrypted session key associated with the user-room pair:

  • If an encrypted session key exists, it is decrypted using the client’s private key, and the E2EE session begins.

  • If no session key exists, a new AES-GCM session key is generated using:

crypto.generateKey({name: 'AES-GCM', length: 256}, true, ['encrypt', 'decrypt'])

Once created, the session key must be distributed securely to all users in the room:

  1. Fetch public keys for all users in the room using the getUsersOfRoomWithoutKeys() server method, which returns each user’s ID and public key.
    This is done in batches, meaning some users may receive their keys earlier than others.

  2. Encrypt the new session key with each user’s public key.

  3. Share the encrypted key by calling provideUsersSuggestedGroupKeys(). This adds the encrypted session key as a suggestedKey to each user’s subscription.
    This process works asynchronously, users do not need to be online at the same time. As long as one user with the session key remains online, key distribution continues until all users with valid public keys have received their suggested keys.

When users log in, they receive the suggested keys through their subscriptions. Each client evaluates the keys and either accepts or rejects them:

  • If accepted, the suggested key is moved to the E2EKey property in the user’s subscription, enabling encrypted communication.

  • If rejected, the suggested key is removed, and the user is added to a virtual queue awaiting key distribution from other users.

After these steps, the E2EE session is successfully established using the new session key.

4. Sending encrypted messages

When a user sends a message, the onClientBeforeSendMessage event is triggered. If E2EE is active:

  • The message content is encrypted using the session key.

  • The encrypted message is placed inside an object with the following structure:

final_message: {
  msg: <encrypted_message>,
  t: "e2e"
}

Here:

  • msg: contains the encrypted message.

  • t:  identifies the message type ("e2e") to distinguish it from unencrypted messages.

This object is then sent to the recipient, ensuring only the intended recipient can decrypt and view the message. The original plaintext message remains inaccessible to others.

5. Receiving encrypted messages

When a message is received, the onClientMessageReceived event intercepts it.

  • If the message type (t) is not "e2e", the message is plain text and requires no decryption.

  • If the message type is "e2e", the client decrypts the msg field using the session key.

If the receiving client does not already have the session key in its local storage, it retrieves the encrypted key from the server using the fetchGroupE2EKey method. The client then decrypts it with its private key and uses the resulting session key to decrypt incoming messages.

APIs

Rocket.Chat provides several server methods that support E2EE operations, including key generation, distribution, and session management.

This section summarizes the available E2EE-related APIs for context. For the complete and most accurate specifications, including request/response payloads, parameter descriptions, and examples, refer to the official E2E API documentation.

The following methods are available for managing E2EE keys and related operations.

1. e2e.setUserPublicAndPrivateKeys

Saves the user’s public key and encrypted private key to the server.

Request:

key = {
  RSA-PubKey: <public_key>,
  RSA-EPrivKey: <encrypted_private_key>
}

Response:

{ success: true }

2. fetchMyKeys()

Retrieves the current user’s public key and encrypted private key from the server database.

Request:

{}

Response:

key = {
  RSA-PubKey: <public_key>,
  RSA-EPrivKey: <encrypted_private_key>
}

3. e2e.getUsersOfRoomWithoutKey

Returns the list of users in a selected room who are waiting for E2E keys, including their public keys and user IDs.

Request:

rid = the room's ID

Response:

{
  success: true,
  users: [
    { userId: 'xxx', public_key: 'xxxx' }
  ]
}

4. e2e.setRoomKeyId

Defines the keyID for a room. The keyID is derived from the encryption key and is used to identify the correct encryption key in rooms with multiple room keys.

Request:

rid = <room_id>
keyID = <key_id_for_room>

Response:

{ success: true }

5. e2e.updateGroupKey

Updates a user’s Group (Session) Key. When a user creates a new conversation and generates Session Keys, they define this Session Key in their own subscription. After that, they can share the key with other users.

If the uid parameter differs from the user making the call, the encrypted key is defined as that user’s Suggested Key.

Request:

uid = <target_user_id>
rid = <room_id>
key = <encrypted_session_key>

Response:

{ success: true }

6. e2e.acceptSuggestedGroupKey

Marks a validated Suggested Key as valid and sets it as the user’s active Session Key. If there are any oldKeys associated with the user, they are also accepted.

Request:

{}

Response:

{ success: true }

7. e2e.rejectSuggestedGroupKey

Rejects a Suggested Key after validation. The key is removed from the user’s subscription, and the user is placed back into the waiting queue for key distribution.

Request:

{}

Response:

{ success: true }

8. e2e.provideUsersWithSuggestedGroupKeys

Provides users in a room who are waiting for keys with a Suggested Key. The suggested key is stored in the E2ESuggestedKey property of each user’s subscription, allowing them to validate it the next time they come online.

Request:

usersSuggestedGroupKeys = {
  userId: <user_id>,
  key: <suggested_encrypted_session_key>,
  oldKeys: [<array_of_old_keys_used_in_room>]
}

Response:

{ success: true }

9. e2e.resetRoomKey

Allows an authorized user to reset the Session Key for all users in a room.

Request:

rid = <room_id>
e2eKey = <new_session_key>
e2eKeyId = <new_session_key_id>

Response:

{ success: true }

Push notifications of E2EE messages

Overview

Push notifications for messages in E2EE rooms contain only the encrypted payload of the message. Decryption of this payload occurs locally on mobile clients (iOS and Android) before the message is displayed.

Process

The server is responsible for sending push notifications. However, it does not store unencrypted message content from E2EE rooms, only the encrypted message string. These encrypted strings can be decrypted only with the user’s private key, which remains securely stored on their device.

When a new push notification for an E2EE message arrives, it includes the following parameter:

messageType: 'e2e'

Upon receiving such a notification:

  1. The mobile client identifies the message as encrypted.

  2. It attempts to decrypt the payload using:

    • The user’s locally stored private key, and

    • The E2EE key associated with the room from which the message originated.

  3. If both keys are available, the client decrypts the message locally and displays the plaintext content.

Throughout this process, only the encrypted message content is transmitted via push notification gateways, never plaintext data.

This feature is available in the Community Edition.

Fetching full message content from the server on receipt

This feature is exclusive to the Enterprise plan.

For enhanced security, the Enterprise plan introduces an additional push notification mode: Fetch full message content from the server on receipt

In this mode:

  • The push gateways (e.g., Google, Apple, or others) do not transmit any message content, encrypted or otherwise.

  • Instead, only a notification trigger is sent, indicating that a new message should be fetched.

  • Once the Rocket.Chat client receives this trigger, it retrieves the message content directly from the server and displays it as a push notification.

This approach ensures that no message data, even in encrypted form, passes through external push notification services.

Multiple room keys + reset room keys

Overview

Due to the nature of End-to-End Encryption (E2EE), if all users lose their Session Keys for a conversation, the previously encrypted messages become unrecoverable unless the encryption is somehow broken, which is typically not feasible.

The same principle applies in Rocket.Chat. If all participants in a conversation lose access to their Session Keys, that conversation is permanently inaccessible. However, even though past messages cannot be decrypted, it may still be necessary to continue communication in the same room without creating a new one or disabling E2EE.

To address this scenario, Rocket.Chat supports resetting encryption keys for a conversation. When a user resets the Session Keys for a room, all participants’ previous keys are removed from their subscriptions, allowing them to receive a new key and continue the conversation seamlessly.

The process proceeds as follows:

E2EE room key reset flow

Reset process

  1. User A resets the Session Key in room Z.

  2. User A creates a new Session Key and derives a new KeyID.

  3. The server removes the E2EKey property from all users in the room.

  4. A new room key is distributed to the other users, allowing the conversation to continue securely.

The terms Session Key and Room Key are used interchangeably in this document.

Handling existing keys

If some users still have an E2EKey on their subscription, the server cannot determine whether it remains valid. To prevent potential data loss, Rocket.Chat moves the user’s E2EKey to a new property called oldRoomKeys.

This array contains the keys that existed on the user’s subscription at the time of a room key reset. Clients use this information to attempt decryption of old messages.

Decryption process

  1. Each message consists of:

    keyID + encrypted content
  2. The client attempts to match the keyID prepended to the message with the keyID stored in the room object (current Session Key).

  3. If the keyIDs match, the client uses the current Session Key to decrypt the message.

    • If decryption fails, the UI displays the message: Messaage cannot be decrypted due to a key error.

  4. If the keyIDs do not match, the client searches for a key with the given keyID in the oldRoomKeys list:

    a. If a matching key is found, the message is decrypted using that key.

    b. If no match is found, the client attempts to decrypt using the latest key.

    c. If all attempts fail, the message is considered indecipherable.

The same decryption logic applies to file encryption.

Managing old room keys

If a user has old room keys, the key suggestion process accommodates them by allowing the reuse of old keys in the same way as current encryption keys.

The oldRoomKeys array is limited to 10 elements. When this limit is exceeded, the oldest key is deleted, which may render some older messages indecipherable.
When this occurs, the client displays the following message: This message cannot be decrypted due to multiple room key resets.

FAQs

If there are still users with an E2EKey for the room, why not wait for them to come online and share the key?

Waiting for those users is not always practical or reliable, especially if they’ve lost access to their device, uninstalled the app, or never reconnect. Resetting the room key ensures the conversation can continue securely for all active participants.

If I need to lose my keys to see the Reset Room Key button, does that mean I’ll never have old room keys?

Not necessarily. The Reset Room Key button appears when the client no longer has access to the valid Session Key.

However, when a reset occurs, previous keys are moved into oldRoomKeys, allowing you to decrypt older messages (until the retention limit is reached).

What happens to older encrypted messages after a room key reset?

After a reset, old messages remain stored on the server in their encrypted form. However, they can only be decrypted using the corresponding old room keys (if still available in oldRoomKeys). Once those keys are purged (after 10 resets), the older messages become permanently indecipherable.

Who can perform a room key reset?

Only users with the appropriate authorization level (for example, room owners or administrators) can initiate a room key reset. Regular users cannot trigger this operation.

Does resetting a room key affect users who are offline?

Yes. When those users come back online, their old E2EKey is automatically moved to oldRoomKeys, and they’ll receive the new session key via the standard key distribution process.

Can bots or integrations access messages after a key reset?

Not unless explicitly configured for E2EE support. Since bots do not automatically receive new keys, they lose access to both old and new encrypted messages unless reauthorized.

How many times can I reset room keys before messages are lost?

You can reset up to 10 times before the oldest oldRoomKeys entry is removed. Once the 11th reset occurs, any messages encrypted with the removed key become permanently unreadable.

Can I export or back up my E2EE keys?

Rocket.Chat does not export encryption keys directly for security reasons. Users should securely store their mnemonic recovery phrase, which allows re-deriving the master key when needed.