We recently wrote an encryption extension to AppFriends, and working together with our AppFriends SDK, we were able to put together a sample end to end encrypted chat app in less than 3 days. You can find the app here.
After we launched our in app chat service, AppFriends, there’s been a lot of interest around how to make the chat content completely private and secure. The team moved quickly and identified what we needed to do:
- need asymmetric encryption, and only the user should have the key to decrypt the message sent to him/her
- when a message is received, there has to be a way to verify the origin of the message
- in a group chat, a message must be encrypted in a way that everyone in the group can decrypt
- some of our customers would still want to be able to see the chat content, so the encryption must be an optional feature which can be easily turned on/off. AppFriends should not have any dependency on third party encryption service.
Goal 1: Total Privacy — only the user has the key to decrypt a message
Step 1: We generate the private-public key pairs in the app on the user’s device.
Step 2: private key is saved in the device’s safe storage only
Step 3: public key is published to our server so that they can be used by others to encrypt the messages
Result: Since the private key is saved only on the device, only the user of the device can have access to it. It means unless the user lost both the device and the password to the device to someone, nobody can decrypt the message encrypted by the public key. Even though the encrypted messages are stored on our server, we or any third party have no way to read them.
Goal 2: Verify the origin of the message
Since the public keys are public, potentially anyone can send a message with your public key and pretend to be you. As a result, it’s absolutely necessary to authenticate the origin of the message.
Virgil provides very convenient API for a user to sign the message with private key before sending it. At the receiver end, we only need to authenticate the signature on the message with the public key to verify the origin of the message.
Goal 3: Group chat messages must be readable by all users in the group
With Virgil’s API, we can encrypt the message with all public keys of the members of the group. The message can then be decrypted by any one of the private keys of the users in the chat group.
There’s a small catch: if a user joined a group chat after there’s already some conversation happened, the newly joined user cannot read the messages that was sent before he joined. We believe this is going to be fine for most applications.
Goal 4: Encryption should be optional, and AppFriends should not have any dependency on Virgil
To make AppFriends not depend on Virgil, we made the encryption function as an extension to AppFriends rather than including it inside AppFriend SDK. We started by defining protocol/interface of the encryption extension, so AppFriends SDK can simply call the extension which implements the protocol/interface, and not care about how they are implemented. Thus, AppFriends is not dependent on Virgil. Potentially, the extension can be done using other services similar to Virgil.
If an implementation of the extension is not provided to the AppFriend SDK, it can just assume encryption is not needed, and simply not send messages through the encryption extension. Our customers who do not need encryption can still enjoy the old services without changing anything.
Room for Improvement
With the current setup, the private key is stored on the user’s device. If the user loses the device, he will no longer have access to the messages he sent/received. We can use multi-factor authentication to allow users to add new device after the old one is lost, but since we can’t recover the private key, the old messages cannot be decrypted anymore.
Thank you for reading! If you have any comment or suggestions, please let me know. Also let me know if you are writing or need a chat function in your app.