Note: The Virgil SDK used in this tutorial has been replaced by the Virgil Security E3Kit SDK. Please follow the Firebase guide found here.
Hi there. We’re the team behind Twilio’s end-to-end encrypted HIPAA-compliant messaging and many health and IoT products that use the awesome innovation of end-to-end encryption to build a whole new world of privacy-first products.
After years of hearing from developers who wanted to implement HIPAA-compliant secure chat apps on their favorite platform (Firebase), we decided to do something about it.
First of all, we took the approach that worked for Twilio – build it in a way that doesn’t require a HIPAA Business Associate Agreement (a “BAA” which means you assume the liability for the security of the personal data). We did this by preventing you the developer or us the tech provider from seeing or storing any data - even encrypted health data - beyond the message delivery. Then we built these techniques into iOS, Android and JavaScript sample apps and audited the approach and implementation with RedLion.io, experts in InfoSec & HIPAA compliance.
In this post, we’ll walk you through how end-to-end encryption works with Firebase, share the link to our Firebase Chat HIPAA whitepaper and invite you to try the iOS and Android in-app chat samples we’ve built for you. If you have any questions, join our Slack channel and chat with us!
Shortcut alert! If you want to cut the chase, download the samples by signing up for a free Virgil account at https://VirgilSecurity.com/getstarted and follow the steps for an End-to-End Encrypted app on Firebase.
How do we meet HIPAA’s requirements?
We used Virgil Security’s end-to-end encryption SDK in a way that’s compliant with HIPAA’s three relevant provisions regarding electronic Protected Health Information, known as “ePHI:” the HIPAA Privacy Rule, the HIPAA Security Rule and the HIPAA Breach Notification Rule.
And, again, we’ve done this in a way that doesn’t require you, Virgil Security or Firebase to sign a HIPAA BAA. Check out our Firebase-specific whitepaper that explains the legal side of things: https://VirgilSecurity.com/firebase-whitepaper
The inevitable 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 organization requirements such as training for your team, recovery plan in case systems are down, etc. Make sure that you understand all these requirements found on the Health and Human Services website. We’ll cover the data security front, but there’s still some work for your HR & operations folks!
What is End-to-End Encryption?
First, let’s start with a quick refresher of what end-to-end encryption (“E2EE”) is and how it works.
This is what your Firebase app looks like today:
Data is only encrypted as it goes over WiFi and the Internet, but unencrypted elsewhere. Technically, every server and database service along the way can access your users’ plaintext chat messages. When cloud service providers add an extra layer of security by using “at rest” encryption, it only means that the database file is encrypted on disk with a key (or keys) that they can access. Why is this a problem? 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 their cloud infrastructure, end-to-end encryption is a layer on top that protects developers from mistakes and hacks.
And this is what your app will look like after you implement client-side end-to-end encryption:
The messages are encrypted on the user device and remain encrypted as they travel over the mobile network/Wi-Fi/Internet, through the cloud/web server, into Firestore, and on the way back to your chat partner (such as a patient or doctor in a healthcare scenario). In other words, none of the networks or servers will have a clue what the two of you are chatting about. It’s the WhatsApp way.
What can I end-to-end encrypt?
Anything. Really. Chat messages, file or data transfers (health records between hospital and insurance provider), even live streams. In this example, we keep our focus on In-app Chat in healthcare.
Can I just do it myself from scratch?
What’s difficult in end-to-end encryption is managing the encryption keys in a way that only the users involved in the chat can access them and nobody else. And when we say “nobody else,” we really mean it; even your cloud provider or you, the developer, are out; no accidental mistakes or government-ordered peeking are possible.
Writing crypto, especially to work across multiple client platforms (iOS, Android, JS), is hard. Generating truly random numbers, picking the right algorithms for the operating system/browser, and choosing the right encryption modes are just a few examples of why most developers wave the white flag and end up NOT doing it. It famously took WhatsApp 3 years and 15 smart developers to make their chat app end-to-end encrypted, on par with project timelines at Facebook Messenger, Apple and other tech companies.
Our SDK makes it possible for you to implement an end-to-end encrypted chat proof of concept in 5 minutes and the production feature in a matter of days, getting you to HIPAA compliance as quickly and safely as possible.
Okay, how do I end-to-end encrypt my Firebase chat app?
In a nutshell (and in Swift):
1) For every new user, the app generates a private & public key in your app (on the client device):
let keyPair = try self.crypto.generateKeyPair()
2) The public key is published to our Cards Service, so that other users of your app can retrieve it to encrypt data for you:
Before you’d ask – no, we don’t send up the private key to the server.
cardManager.publishCard(privateKey: keyPair.privateKey,
publicKey: keyPair.publicKey,
identity: identity)
3) The private key remains on your device and lives in your mobile operating system’s native key store:
try self.keyStorage.store(privateKey: keyPair.privateKey,
name: identity, meta: nil)
4) Before sending a chat message, the app encrypts it with the recipient’s public key:
return try self.crypto.encrypt(data,
for: self.channelKeys + self.selfKeys).base64EncodedString()
5) After receiving a message, the app decrypts it with your user’s private key, which never leaves the device:
let decryptedData = try self.crypto.decrypt(data,
with: privateKey)
The technique above ensures that only end-users are able to see messages; the data won’t be visible to Firebase, Google or developers/IT staff at your company. It’s now an end-to-end encrypted chat, like your WhatsApp conversations.
What if I’m building a group chat?
If you’re implementing group chat and you want to avoid encrypting all messages with all participating users’ public keys, and then client-side re-encrypting them when a new user joins the conversation, do this:
- Create a new symmetric key pair (generateKey, i.e. the same key is used for encryption & decryption) for every new chat thread,
- Use that key to encrypt/decrypt all messages in the thread. It’s symmetric to prevent non-participating users from being able to write messages to the thread.
- Encrypt the key with all participating users’ public keys and store the encrypted key in Firestore (which is fine, because it can’t be decrypted without the users’ private keys). This way, you cryptographically share the thread key with all users in the thread. Ping us on Slack if you have any questions or need help implementing this.
Is that all I need to make my Firebase chat app HIPAA compliant?
In addition to all the non-technical HIPAA requirements that are not covered here, there’s a couple more things you need to do.
- First of all, you need to authenticate your users, which isn’t surprising.
- Then to maintain your users’ privacy, you should not use your users’ email addresses or any other personal data as usernames and you shouldn’t store any of their personal data in your Firebase backend. This is how we implemented our sample apps (and how they passed the audit), so you have code to reuse for this.
- And there’s one more thing that you need to implement to cross your t’s and dot your i’s: Message Redaction. Since patient chat contents are client-side encrypted with keys that never make it to your backend, it’s practically impossible to tell whether the Firestore-stored messages on the backend are sensitive health data or something totally benign. But this isn’t enough for HIPAA. So in order to meet HIPAA’s requirements, we implemented a data redaction feature in the sample apps. This feature deletes messages immediately following a successful message delivery to all users. This way, HIPAA considers your chat to be a “conduit,” i.e. only a message delivery tool that doesn’t store the data beyond delivery, which exempts the messaging function completely from HIPAA.
For the important details, check out our whitepaper at https://VirgilSecurity.com/firebase-whitepaper.
What happens if my user loses her device with the private key on it?
As in the sample, your messages are local-only (Firebase is only an intermediate delivery system), so the messages will be lost with the phone. However, if there’s (non-patient) data that you want to permanently store encrypted in Firebase, there’s a solution for lost devices/keys.
Drumroll, please… Imagine a private key that can be lost, and you don't have to worry about losing your encrypted data. Or, imagine a private key that you can use between devices without needing to transfer the key around (WOW!). This special key is called “BrainKey”: a strong cryptographic key that’s based on your user’s PASSWORD. Every time you capture your user’s password, you can re-generate the same private key, so it won’t be lost with the device:
let brainKeyContext = BrainKeyContext.makeContext(
accessTokenProvider: accessTokenProvider)
let brainKey = BrainKey(context: brainKeyContext)
// Generate default public/private Curve ED25519 keypair
let keyPair = try! brainKey.generateKeyPair(
password: "Your password",
brainKeyId: "Your id here").startSync().getResult()
Here’s how to implement your user private key recovery:
- At signup, generate a BrainKey right after you generate your user’s private key.
- Encrypt the user’s private key with her BrainKey’s public key.
- Store the encrypted private key in your Firestore where you store the user’s profile data.
- At login-time, re-generate the BrainKey from her password.
- Download the encrypted user private key from Firestore.
- Decrypt it with the BrainKey, and Voila! You have a way to recover the key if a device is lost.
How do you deal with a forgotten password and lost device at the same time? Use a concatenated string of 3 secret answers as a password to generate a secondary BrainKey for your users. Use this secondary BrainKey in the same way as you do the password BrainKey: encrypt, then later “unlock” the user private key. Now you have a user lockout solution with 3 secret questions+answers!
Get started on your HIPAA-compliant secure Firebase Chat app
We've put together a video tutorial to help you get started with our Firebase SDK. First, register for a free Virgil account at https://VirgilSecurity.com/getstarted and follow the steps to create an End-to-End Encrypted app.
If you have any questions, join our Slack channel and chat with us!
Virgil Security, Inc. enables developers to eliminate passwords & encrypt everything, in hours, without having to become security experts. Learn more at VirgilSecurity.com.