How to build end-to-end encrypted chat with Sendbird and Virgil
This tutorial will show you how to apply Virgil’s E3Kit group encryption to your application’s chat messages using Sendbird’s UIKit. Before diving into the implementation details, we’ll first understand more about end-to-end encryption and take a closer look at Virgil Security. We’ll then cover a core use case and illustrate it with a user story.
But first, let’s take a look at some prerequisites.
To get the most out of this tutorial, it is helpful to have:
- A React.js-based application to run UIKit
- A functioning server to issue JWTs (JSON Web Token) to Virgil users. Please read the Virgil docs to understand how a JWT works. This GitHub repo contains sample code that shows how to generate a JWT.
With that out of the way, let’s get started!
What is end-to-end encryption?
End-to-end encryption is a secure communication system in which only users with a private security key can read transmitted messages. End-to-end encryption is often required in regulated industries, such as healthcare or finance, with compliance requirements on data accessibility. End-to-end encryption encrypts the message payload transmitted, stored, and retrieved. Only users with access to the private key used to encrypt the data can read the content.
In the context of Sendbird, end-to-end encryption means that Sendbird systems and personnel are unable to access the message’s content. This is a critical part of Sendbird’s privacy guarantees. It is worth noting that all communications in Sendbird are encrypted at the transport level while in motion and at the persistence level when at rest.
Consider the case of a healthcare company. HIPAA compliance is mandatory in healthcare. Patient data needs to be secured by all means.
The user story goes: As a user, I want to send messages that can only be read by me and the persons I’m sending the messages to protect my privacy. End-to-end encryption delivers on the user requirement.
Remember to consider the impact of sending encrypted messages. Encrypted messages can be used for good purposes or to harm. Make sure to check the legal implications for implementing encrypted messages in your jurisdiction.
If you want to learn more about end-to-end encryption, check out this excellent article by the Electronic Frontier Foundation.
Now that the foundations are laid, let’s discuss why you should consider using Virgil’s E3Kit with Sendbird.
Why use Virgil’s E3Kit with Sendbird?
Virgil Security is a cryptographic software and services provider. It offers E3Kit, an end-to-end encryption SDK for many platforms to simplify encryption, key management, and other complex security tasks.
- Total privacy: Only users can read their messages; Sendbird, Virgil, and any other third-party services cannot decrypt messages and data to access private information.
- End-to-end encryption: The user’s data is protected at rest and in transit.
- Independent data protection: With E3Kit, any attacks on a 3rd party service provider and their network won’t influence the data integrity and confidentiality.
- Data integrity: The E3Kit signs and verifies data as part of the encrypt and decrypt functions, confirming that the data is coming from the user who encrypted it and that it wasn’t tampered with during transit or while in storage.
Broad implementation overview
The implementation consists of 3 major parts:
- Register users
- Create Sendbird Group Channel and Virgil Group
- Encrypt and decrypt messages in Sendbird’s UIKit
First, let’s go through a quick overview and then dive deeper into the implementation with code samples.
- Collect a Virgil JWT
- Register the user’s device with Virgil
The diagram below shows the high-level communication flow. Remember that a counterpart message sender user is required for the registered device.
Create Sendbird Group Channel and Virgil Group
Given a Virgil user_id and Sendbird user_id are the same:
The current user:
- Creates a Sendbird Group Channel with other users
- Creates a Virgil Group with other users, and the Sendbird channel URL is the same as the Virgil Group ID
The current user is automatically:
- Set in the Sendbird Group Channel created_by field
- Set as the Virgil Group owner
Encrypt and decrypt messages in Sendbird’s UIKit
- Load a Sendbird Group Channel
- Fetch the Sendbird Group Channel URL and create_by user_id. Use them to load the corresponding Virgil Group
- Use the loaded Virgil Group to:
- Decrypt any messages in the fetched Sendbird Group Channel
- Encrypt new outgoing messages
- Decrypt newly arriving messages
Now let’s go through all these steps in detail.
Step 1. Register users
Given that you have a Web App server running to issue Virgil User JWT tokens, run the following code.
We need to get a JWT and init Virgil instance, and fetch a JWT token when the app loads.
Step 2. Create Sendbird group and Virgil group
- Utilize Sendbird’s UIKit built-in onBeforeCreateChannel method
- Generate a random channel_url. Consider using a UUID.
- Start creating the Virgil Group. Please note that the onBeforeCreateChannel method native to the Sendbird UIKit is synchronous. Therefore, wait for Virgil Group to be created. Enter any blocking code.
- Do not include the current user in participant identities. Only have the user list provided by the onBeforeCreateChannel’s argument.
- Return the required Sendbird Group Channel params without any delay from onBeforeCreateChannel.
e3.js – Create Virgil Group
CustomizedApp.js – Synchronous generation of channel params
Step 3. Encrypt and decrypt messages in Sendbird’s UIKit
1. On Sendbird channel select, load the associated Virgil Group
- Sendbird’s UIKit has a method called onChannelSelect, which fires when the UIKit channel list first loads and when a user clicks on a channel in the channel list.
- onChannelSelect is synchronous. Therefore, load the Virgil Group without a callback. The loaded Virgil Group will automatically reside in the E3 instance for cipher operations.
- Consider implementing your own error handling.
Important note: To load the correct Virgil Group, you will need the Sendbird Channel URL and the user_id of the creator of the Virgil Group. In this case, the details are stored in the Sendbird channels’ creator field.
2. Load custom message input
Sendbird’s UIKit message input currently does not allow for asynchronous operations before message sending. Therefore, it is recommended to leverage Sendbird’s useSendbirdStateContext with the sendbirdSelectors.
Render the custom message input and pass down the Sendbird SDK in order to access useSendbirdStateContext and sendbirdSelectors.
Check out the entire input component in the code here.
3. Encrypt a message which automatically updates in the channel’s message list view
When a user sends a message, the following operations take place:
- The message is composed for sending and the user clicks send (input placeholders are reset).
- The message is encrypted, which requires getting the corresponding Virgil group loaded in step 2 above.
- A Sendbird message instance is created.
- The UIKit’s sendbirdSelectors are leveraged to send the message and will automatically update the message view (more in the next step).
- Note that the message is marked as encrypted by setting the message’s built-in custom_type (String)
4. Decrypt messages and automatically update the message list view
With Sendbird’s UIKit, it is possible to listen for changes to the message list view and render a message card for each new message. The customer message card can be created by leveraging the UIKit’s renderChatItem property on the Channel component.
- The raw encrypted message text is passed in.
- The current user_id is passed in and used to detect if the current users sent them.
- The E3 instance is included.
- The current channel is added as its channel_url and created_by fields will be needed for decryption.
The decryption of each message is asynchronous. Therefore, we set placeholders for each message, complete the decryption, then re-render the unencrypted messages.
- Render all messages with placeholders.
- Check if each message is encrypted.
- Decrypt each message.
- Re-render the messages with the decrypted text placing them left or right depending on who sent the message.
Notice how the message sender’s user_id is required. Then we get the Sendbird Group Channel’s associated Virgil Group. Finally, the message can be decrypted and returned.
And that’s it! We have covered how to associate a Virgil Group with a Sendbird Group Channel and use Sendbird’s UIKit customizations to encrypt, decrypt and display decrypted messages. With Sendbird’s UIKit and Virgil Security, you can rest assured that all your users’ messages are secure.
If you need additional guidance, check out the docs for both Sendbird and Virgil. As always, don’t hesitate to contact us if you need any help!
We can’t wait to see what you build! Happy coding! ✌️