This blog post introduces a plugin that provides end-to-end encryption (E2EE) to Mattermost.
Until summer 2021, Quarkslab was still a hardcore user of IRC. After many years of loyal service, we finally decided to migrate to a more modern system: Mattermost. Mattermost is often described as an open source Slack, and matched many of the new criteria we wanted. Only one feature was missing: end-to-end encryption (E2EE).
End-to-end encryption allows messages to be authenticated, encrypted and decrypted on the users' devices. This means that the server only sees encrypted messages it can't decrypt. It also guarantees the identity of the sender of the messages, so that the server cannot impersonate them. This brings privacy to Quarkslab's employees, and can help prevent information leakage. These principles are widely used in messaging apps like Signal, Olvid or WhatsApp.
This blog post discusses the attack models we consider, some prior art, the design of this plugin, current known limitations and future work.
The plugin is open source on Github: https://github.com/quarkslab/mattermost-plugin-e2ee. You can follow these instructions to install it on your own Mattermost instance. A comprehensive end-user documentation is also provided.
In this attack model, we consider that the attacker either has access to the data that the server receives (but won't modify them), or can somehow read the database of the Mattermost server (but can't/won't write anything to it).
In this attack model, the attacker has full control over the server, or communications between clients and the server. It means that it can, among other things:
- deliver fake public keys for some users (some form of MiTM), and get messages encrypted for a private key he owns
We discuss these issues in details in the following sections.
An end-to-end encryption plugin has already been written, named anonymous.
We didn't use this plugin for various reasons:
- It uses the node-rsa module, which is a pure JS implementation of the RSA algorithm. It can't have the nice non extractibility property that the WebCrypto API has.
- The choice of whether a message is encrypted or not is done per message by the user. We wanted a per-channel and server-enforced configuration. Moreover, there's no UI difference between an encrypted message and a classical one.
- No authentication is done on the sent messages.
That being said, this plugin has been a great inspiration to us!
Cryptographic scheme & compromises
There are a few problems common to all E2EE systems:
- How to make it work on all the user's devices in the most transparent way?
- How the public keys of each user are stored & trusted.
Let's discuss them!
Public keys storage & trust
The second general problems boils down to trust: if we consider the server to be evil, how can we be sure it is serving legitimate public keys? GPG for instance solves this problem with a web of trust, having each individual digitally sign other individuals' public keys as they trust them, creating a graph of trusted relationships.
In our case, we inspired ourselves with what applications like WhatsApp or Signal do, which can be summarized by Trust On First Use (TOFU). The idea is that each device starts with an empty public key database, and fills it as it gathers users' public keys from the server. If at some point one of them changes (meaning that the server returns a different one for a known user), the web app plugin shows a message to alert the user. This is the equivalent of the "Security code has changed" message of WhatsApp for instance.
For now, we have one encryption mode, that we call P2P. In this mode, each message is encrypted for each member of the channel and then signed. There is no per-channel encryption key shared among the participants. We might develop this channel shared key   mode in the future.
The complete protocol is described in a design document.
There is a lot more that could be said on all these problems, and it will deserve a complete blog post of its own, describing all the various solutions that already exist. Stay tuned!
Let's now describe some currently known limitations.
The web app integrity problem
Fixing this is work-in-progress, and any help or suggestions would be appreciated! We track progress on this matter in this ticket.
The plugin has other known UX limitations, that are explained in detail in the project's README:
Future work will mainly focus on fixing the known limitations. We will also work to improve the overall user experience, especially around private key generation and/or import.
We also have some idea to make sure we don't use too much crypto (and still be secure obviously), and try to reduce the CPU usage while encrypting/decrypting messages.
We'd like to greatly thank the Mattermost community & developers for having answered our questions while developing this plugin. We especially appreciated the patch that will allow us to implement encrypted messages edition!
Also thanks to my colleagues Béatrice Creusillet, Charlie Boulo, Laurent Grémy, Angèle Bossuat & Damien Aumaitre for their valuable comments & reviews of this blog post!