Many developers have used our existing end-to-end encryption Virgil SDK to secure data in their Firebase apps for compliance with HIPAA and GDPR, to limit developer liability from data breaches, to protect user privacy, and many more economic and ethical reasons.
While we've built the Virgil SDK to be user-friendly, it's a little more customizable than some people need. So we've built a new SDK that comes with key functionalities ready out of the box, and we're calling it “E3kit.” Its name comes from the three e’s in end-to-end encryption, because why not?
E3Kit is an easier-to-use SDK for end-to-end encryption with features like multi-device support already baked in. If you want more control and customization or are working with an IoT device, our original end-to-end encrypted Virgil Core SDK is probably a better fit.
In this post, we’ll walk you through how E3Kit works with Firebase 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 using Cloud Firestore. However, you can use Virgil’s end-to-end encryption SDKs to end-to-end encrypt any 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 Firebase app looks like today:
Firebase uses Google Cloud’s strong security features, including encryption in transit with HTTPS, and encryption at rest for many services, including Cloud Firestore. This means that if an evil-doer were to listen in on the network calls your users make to Cloud Firestore, or if they were to break into one of Google's data centers and make off with a hard drive, they still wouldn't be able to access your users' data.
But this data is unencrypted as it passes through frontend and backend servers, and is also available to admins and developers in the live database. This means that anybody in your company with “view” access to your Cloud Firestore database could still see this data. And if there's an error in your Firebase security rules that allows rogue parties to download documents they shouldn't, those parties could see the data contained in those documents.
A two year study in the UK found that 88% of data breaches were caused by developer error, not cyberattacks. While Google is doing a great job protecting the cloud infrastructure, end-to-end encryption is a layer on top that protects developers from both mistakes and hacks.
End-to-end encrypted Firebase app
This is what your app will look like after you implement client-side end-to-end encryption:
Using a chat app as an example, the messages will be encrypted on the users’ devices and remain encrypted everywhere in between. In other words, none of the networks, servers and databases (not even you) 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 Cloud Firestore 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?
When one of your users signs up, your app will use E3Kit to 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 creates 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 can be found in the E3kit documentation or on the Virgil Security dashboard after you create a Virgil Security developer account and create a new application.
Step 1: Configure your Firebase project
Set up and deploy a Cloud Function to authenticate and register your users with Virgil Security and give them access to the Virgil Cloud.
Step 2: Set up your client
On the client side, we will use the 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 in this step.
Note: These code snippets are in Javascript, but E3Kit works across any language. You can find snippets for Java, Kotlin and Swift in the documentation here.
A) Use the package manager to download the E3Kit SDK to your mobile or web project
// CocoaPods is a dependency manager for Cocoa projects.
// You can install it with the following command:
$ gem install cocoapods
// to integrate Virgil E3Kit into your Xcode project using CocoaPods, specify it in your Podfile:
target '<Your Target Name>' do
use_frameworks!
pod 'VirgilE3Kit', '~> 0.3.0'
end
// then, run the following command:
$ pod install
B) Initialize E3Kit
import VirgilE3Kit
import VirgilSDK
// Virgil-Firebase function URL
let jwtEndpointURL = URL(string: "https://YOUR_FIREBASE_FUNCTION_URL.cloudfunctions.net/api/virgil-jwt")!
// Use Firebase token for authentication on Virgil-Firebase function backend
let headers = ["Content-Type": "application/json",
"Authorization": "Bearer " + firebaseToken]
// This function makes authenticated request to Firebase function
// The token it returns serves to make authenticated requests to Virgil Cloud
let tokenCallback: EThree.RenewJwtCallback = { completion in
let connection = HttpConnection()
let request = Request(url: jwtEndpointURL,
method: .get,
headers: headers)
guard let jwtResponse = try? connection.send(request),
let responseBody = jwtResponse.body,
let json = try? JSONSerialization.jsonObject(with: responseBody, options: []) as? [String: Any],
let jwtStr = json?["token"] as? String else {
completion(nil, AppError.gettingJwtFailed)
return
}
completion(jwtStr, nil)
}
// Initialize EThree SDK with get token callback to Firebase Function
// E3kit uses the identity encoded in the JWT as the current user's identity
EThree.initialize(tokenCallback) { eThree, error in
// Init done!
}
Step 3: Register users with Virgil Security
eThree.register { error in
guard error == nil else {
// Error handling here
}
// User private key loaded, ready to end-to-end encrypt!
}
Step 4: Encrypt message data
let usersToEncryptTo = [user1UID, user2UID, user3UID]
// Lookup user public keys
eThree.lookupPublicKeys(of: usersToEncryptTo) { lookupResult, errors in
guard errors.isEmpty else {
// Error handling here
}
// Load data to memory
// ...
// Encrypt data using user public keys
let encryptedData = try! eThree.encrypt(data: data, for: lookupResult!)
// Encrypt message using target user public keys
let encryptedText = try! eThree.encrypt(text: "Hello, team", for: lookupResult!)
}
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 matches the sender’s public key.
// TODO: Get users' UIDs
// Lookup user public key
eThree.lookupPublicKeys(of: [bobUID]) { lookupResult, errors in
guard errors.isEmpty else {
// Error handling here
}
// Decrypt data and verify if it was really written by Bob
let originData = try! eThree.decrypt(data: encryptedData, from: lookupResult[bobUID]!)
// Decrypt text and verify if it was really written by Bob
let originText = try! eThree.decrypt(text: encryptedText, from: lookupResult[bobUID]!)
}
Voila! You’ve end-to-end encrypted messaging in a Firebase app.
You can find instructions for additional features like password changes and device cleanup in the integrated tutorials within the Virgil Security dashboard.
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 that can also be the bad news...
- 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).
How to Build for HIPAA Compliance
End-to-end encryption can be used to comply with HIPAA’s three relevant provisions regarding electronic Protected Health Information, which is explained in more detail here and in the accompanying whitepaper.
This is achieved via a two-pronged approach:
- End-to-end encryption of ePHI secures the data and de-identifies the data.
- Immediate redaction of message data (including metadata) can allow you to be classified as a courier (called “conduit” in HIPAA language), which can exempt you from HIPAA altogether (unless your product is handling ePHI in other ways).
Important: Message redaction is not built into E3Kit or Firebase automatically, so you’ll need to explicitly implement that in your product.
Virgil Security’s SDK encrypts and authenticates data from Point A to Point B. If your product is more complex and your HIPAA compliance needs extend beyond just protecting the ePHI in your chat/messaging function, you'll probably need to use a HIPAA-compliant cloud like Google Cloud Platform and then use Virgil's SDK to layer end-to-end encryption on top of that.
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 encrypting Firebase apps.
Questions? Feel free to ask the Virgil Security team on firebase-talk or on Slack.
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 Virgil Security use cases.
The inevitable but important disclaimer: implementing your app’s data security according to this tutorial does not necessarily mean that your whole project is HIPAA compliant. HIPAA is more than just data security. It has organizational requirements such as training for your team, a recovery plan in case systems are down, etc. Make sure that you understand all these requirements found on the Health and Human Services website. The sample will help you on the data security front, but there’s still some work for your HR & operations folks.