e3kit: A New SDK to Make End-to-End Encryption Even Easier

Rebecca YarbroughJanuary 16th, 2019

Many developers have used our existing end-to-end encryption Virgil SDK to secure data in their products for compliance with HIPAA and GDPR, to limit developer liability from data breaches, to protect user privacy, and for many more economic and ethical reasons.

While we built the Virgil  SDK to be user-friendly, it’s more customizable than some projects need. So we’ve built a new option called “e3kit” that’s simpler to set up and comes with key functionalities ready out of the box.

e3kit is an end-to-end encryption SDK with a simpler set-up process and with features like multi-device support already baked in. If you want more control and customization (like if you’re working with an IoT device or using your own crypto library), the Virgil end-to-end encryption SDK is probably a better fit.

In this post, we’ll walk you through how e3kit works and how you can start building end-to-end encryption into your app. To illustrate the concepts, we’ll use the common use case of a chat app sending messages back and forth between two users. However, you can use Virgil’s end-to-end encryption SDKs to end-to-end encrypt communication between any two endpoints, including mobile apps, web apps, servers, and IoT.

We have resources on our blog and in our Help Center to inspire you and help you troubleshoot while building. If you have questions or want to see what other developers are building, hop on our Slack channel to join the conversation. We can’t wait to see what you encrypt!

What is End-to-End Encryption?

This is what a typical app looks like today:

Without end-to-end encryption, the security gaps along the data path are where data breaches happen.

Today, a significant amount of web traffic is encrypted in transit (HTTP with TLS or SSL) and a smaller percentage of data is encrypted at-rest in the database (encrypted by cloud storage provider, and the key is stored in the database).

While a combination of TLS and at-rest encryption is better than nothing, it’s clear that data breaches still happen daily even with these systems in place.

It’s because there are gaps along the way where the TLS or at-rest encryption starts and stops, as well as the fact that even with at-rest encryption, the key is usually stored in the database. This is like storing a housekey under the doormat. Plaintext data is still accessible to developers and hackers on frontend and backend servers. Plus, governments, ISPs and telcos can see the data. Even if you trust all these people, if there is a technical mechanism that allows them to access it, that technical mechanism is available to anyone to exploit.

In fact, a two year study in the UK found that 88% of data breaches were caused by developer error, not cyberattacks. So if your data is not end-to-end encrypted, all it takes is a developer at some third party service with access to your data to make one implementation error or click through on a phishing email, and your whole database could be breached. End-to-end encryption is a layer on top that protects developers from both mistakes and hacks.

This is what your app will look like after you implement client-side end-to-end encryption:

With end-to-end encryption, there are no security gaps along the data path to expose your product to breaches.

The data will be encrypted on the end devices and remain encrypted everywhere it’s sent and stored until the end user opens the message and decrypts it on her device. Neither you nor any of the networks, servers, databases, or third party services will see anything but scrambled data passing through.

What can I end-to-end encrypt?

Anything – chat messages, files, photos, sensory data on IoT devices, permanent or temporary data. You decide what data you want to end-to-end encrypt — you can encrypt some fields in a document, but not others. For example, you might want to keep benign information related to a chat app (like timestamps) in plaintext but end-to-end encrypt the message content.

How do I implement it?

Just like most things in life, it takes a lot of work behind the scenes to make this end-to-end encryption appear seamless to you and your users. You’ll actually be utilizing a combination of Virgil Security services. The client-side e3kit helps you easily connect to these services, including the server-side Virgil SDK and other Virgil Cloud Services that enable multi-device access, group chat, and other features we’re adding soon.

Using the client-side e3kit SDK, when one of your users signs up, your app will generate a private and public key on their device. The user’s public key is published to the Virgil Cards Service (effectively a cloud directory that stores and manages the public keys) for users to find other users’ public keys and encrypt data for them. Each user’s private key remains on their device and is protected by the operating system’s native key store.

Here’s how to get it up and running in your app. The full code is found on the Virgil Security dashboard once you create a Virgil Security developer account and follow the e3kit end-to-end encryption guide within the dashboard.

Step 1: Set up your backend

You’ll need at least a basic backend server for your app that has persistent storage and user authentication. Then you’ll use one of these sample apps to set up the Virgil SDK on your backend to generate JWTs for each of your application’s users that will identify them and grant them access to the Virgil Cloud – Node.js | Golang | PHP | Java and follow the instructions in README.

Step 2: Set up your client

On the client side, we will use the Virgil e3kit SDK to create and store the user’s private key on their device and to publish the appropriate public key in the Virgil Cloud.

You’ll need to install and initialize e3kit SDK in this step.

A) Use the package manager to download the e3kit SDK to your mobile or web project.

npm install -S @virgilsecurity/e3kit

B) Initialize e3kit:

import { EThree } from '@virgilsecurity/e3kit';

// This function returns a token that will be used to authenticate requests
// to your backend.
// This is a simplified solution without any real protection, so here you need use your
// application authentication mechanism.
async function authenticate(identity) {
    const response = await fetch('http://localhost:3000/authenticate', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({
            identity: identity
        })
    });
    if (!response.ok) {
        throw new Error(`Error code: ${response.status} \nMessage: ${response.statusText}`);
    }

    return response.json().then(data => data.authToken);
}

// Log in as `alice`
const eThreePromise = authenticate('alice').then(authToken => {
    // E3kit will call this callback function and wait for the Promise resolve.
    // When it receives Virgil JWT it can do authorized requests to Virgil Cloud.
    // E3kit uses the identity encoded in the JWT as the current user's identity.
    return EThree.initialize(getVirgilToken);

    // This function makes authenticated request to GET /virgil-jwt endpoint
    // The token it returns serves to make authenticated requests to Virgil Cloud
    async function getVirgilToken() {
        const response = await fetch('http://localhost:3000/virgil-jwt', {
            headers: {
                // We use bearer authorization, but you can use any other mechanism.
                // The point is only, this endpoint should be protected.
                Authorization: `Bearer ${authToken}`,
            }
        })
        if (!response.ok) {
            throw new Error(`Error code: ${response.status} \nMessage: ${response.statusText}`);
        }

        // If request was successful we return Promise which will resolve with token string.
        return response.json().then(data => data.virgilToken);
    }
});

// then you can get instance of EThree in that way:
eThreePromise.then(eThree => { /* eThree.encrypt/decrypt/lookupPublicKeys */})
// or
const eThree = await eThreePromise;

Step 3: Register your users with Virgil Security

// TODO: initialize

await eThree.register();

Step 4: Encrypt message data

In addition to encrypting message data for data security, e3kit uses digital signatures to verify data integrity.

// TODO: initialize and register user (see EThree.initialize and EThree.register)

// aliceUID and bobUID - strings with identities of users that receive message
const usersToEncryptTo = [aliceUID, bobUID];

// Lookup user public keys
const publicKeys = await eThree.lookupPublicKeys(usersToEncryptTo);

// Encrypt data using target user public keys
const encryptedData = await eThree.encrypt(new ArrayBuffer(), publicKeys);

// Encrypt text using target user public keys
const encryptedText = await eThree.encrypt('this text will be encrypted', publicKeys);

Step 5: Decrypt Message and Verify Sender

After receiving a message, we’ll decrypt it using the recipient’s private key and verify that it came from the right sender by confirming that the message signature contains the sender’s public key.

// TODO: initialize SDK and register users - see EThree.initialize and EThree.register

// bobUID - string with sender identity
// Lookup origin user public keys
const publicKey = await eThree.lookupPublicKeys(bobUID);

// Decrypt data and verify if it was really written by Bob
const decryptedData = await eThree.decrypt(encryptedData, publicKey);

// Decrypt text and verify if it was really written by Bob
const decryptedText = await eThree.decrypt(encryptedText, publicKey);

Voila! You’ve end-to-end encrypted messaging in your app.

As we mentioned, Virgil e3Kit SDK supports multi-device support. You can find instructions for implementing that here, as well as support for password changes and device cleanup.

Any Drawbacks?

The good news is that there is no backdoor to this technology, and no one except the private key holders will be able to access encrypted data. And sometimes the bad news is that there is no backdoor to this technology, and no one except the private key holders will be able to access encrypted data…

  • You won’t be able to view the customer data that you’ve end-to-end encrypted. Obviously, this is by design, but it can make things more difficult if you’re looking to troubleshoot a specific customer issue. (Just like users have private keys to access data, you can give admins a private key as well, but you need to do so carefully and be aware of how that impacts compliance with HIPAA and other regulations.)
  • Similarly, third party services won’t be able to do much with data that you’ve encrypted. If there’s data that you’re going to want to run analytics on or access for another business purpose, consider leaving that part of the data unencrypted.
  • There’s a minor performance hit involved in encrypting and decrypting data. Something along the lines of 1-2 ms per message on the client device. Plus, your clients will need network access whenever they want to encrypt that message (user key lookup is an online operation – which you can cache after it’s done).

To get started with e3kit, sign up for a free Virgil Security developer account at dashboard.virgilsecurity.com, create your first application and follow the quickstart guide for end-to-end encryption with e3kit. The e3kit documentation can also be found directly via https://e3kit.readme.ioWe have guides for Twilio, Firebase and PubNub for e3kit as well.

Since Virgil Security’s products are open source, if you have specific feedback, feature requests or code improvements, please log them in GitHub by creating an Issue (https://help.github.com/articles/creating-an-issue/) or making a Pull Request (https://help.github.com/articles/creating-a-pull-request/).

You can find all our repos at https://github.com/VirgilSecurity and specific e3kit repos via readme.io.

Questions? Feel free to ask the Virgil Security team on Slack.