oddsquat/src/pages/posts/2026/encrypted_XMPP.md

14 KiB
Raw Blame History

layout lang year date section title description
post en 2026 2026-04-07 posts encrypted_XMPP Secure and private messaging with XMPP and OMEMO encryption.

End-to-End Encryption in XMPP with OMEMO

I find it funny that twenty years ago I was already trying to promote XMPP over ICQ to my classmates. At the time, the proprietary messenger kept making life harder for users of alternative clients. Thats when I realized that I prefer protocols over services.

I didnt have much success back then, but fortunately, XMPP (and I hope I have too) has continued moving forward over the past two decades. It has developed slowly, sometimes awkwardly, but steadily.

Here I wont talk about why XMPP is great or how to use it. You can check this guide (one of many) and Id rather not write another one. In this post, I want to focus specifically on end-to-end encryption and the practical aspects of using it.

Short Glossary

End-to-end encryption is a way to keep your chats truly private.
Only you and the person youre messaging can read the messages. Not even the server owner has the keys needed to decrypt or modify them.

XMPP is an extensible protocol for instant messaging. It's open, decentralized, and mature.

OMEMO is a widely supported XMPP Extension Protocol (XEP) for secure multi-client end-to-end encryption. You can read more about it on a dedicated page by Daniel Gultsch.

Client, in this post, means a specific instance of an XMPP application on a user device.
OMEMO-related documentation uses the term Device, but I find it potentially confusing: in practice, a single physical device can run multiple independent clients.

Basic Concepts

This section introduces some basic ideas behind end-to-end encryption.

If you're already familiar with the concepts and terminology, you can skip ahead to how end-to-end encryption affects the XMPP user experience or jump straight to the step-by-step workflow I personally use.

Trade-offs Between Safety and Convenience

Unfortunately, things that are truly secure are rarely convenient. They often require some initial efforts and a bit of ongoing attention.

Telegram, which used to be a benchmark for messenger usability before its long dive into enshitification, really draws the line between convenience and security. Regular chats are easy and flexible, but "secret" chats come with a full set of limitations: theyre one-on-one only, cant be synced to another device, arent available on desktop at all, and so on.

All commercial so-called "secure" messengers, like Signal or WhatsApp, end up with pretty similar limitations, because it's tricky to make end-to-end encrypted chats work the way users expect.

Luckily, protocols and cryptography dont care about convenience or user expectations. Many XMPP clients let you do almost anything youre trying to do. Sometimes its clunky and unintuitive, sometimes its the kind of freedom that lets you shoot yourself in the foot. At the end of the day, youd better understand what youre doing.

It might sound messy, but for that price, XMPP actually gives you a lot of handy features: your chats are secured with Signal-grade end-to-end encryption, and you can use as many devices as you want, all at the same time, without being tied to any proprietary service. This post is here to show how to use it intentionally and safely.

In general, the XMPP experience today could be described as a "WhatsApp with benefits and frictions". It's kinda ironic, considering that WhatsApps protocol is actually based on XMPP, but incompatibly altered and defederated.

Keys, Fingerprints and Trust

OMEMO is based on the Double Ratchet Algorithm. While the internal details are quite interesting, for practical purposes it's enough to know that each client stores some cryptographic keys and can derive a hash from them, commonly called a fingerprint.

Keys are usually managed automatically by the XMPP client, and in normal use you should never need to handle them manually. In fact, you probably dont even need to know what they look like. The only thing you need to do is keep them secret and back them up if necessary.

A fingerprint lets you identify a specific client of your contact and verify that it hasnt been spoofed. Fingerprints for an account are not secret: clients publish their own fingerprints to the XMPP server and automatically receive the fingerprints of others. Only fingerprints you explicitly mark as trusted are relevant.

In an ideal scenario, the contact should confirm in person or through an already trusted and secure communication channel that the fingerprint belongs to their device, and only then you mark it as trusted. In most XMPP clients this is simply done by ticking a checkbox or by scanning a QR code.

The list of trusted fingerprints is used at the moment a message is sent. Behind the scenes, OMEMO performs a certain amount of key management, and only the clients that are present in the trusted list at the time of encryption will be able to decrypt the message later.

It's important to understand that trust cannot be applied retroactively: it's not possible to "extend" trust to new clients after a message has already been encrypted and sent.

Practical Aspects of OMEMO and XMPP

Chat History

In theory, XMPP supports server-side message history storage via XEP-0313: Message Archive Management.

In practice, support for this XEP, as well as retention policies and message lifetime, depends on the specific server. You should never assume that all conversations are stored indefinitely by default. From a practical standpoint, the server-side MAM archive is better considered a cache: it can help you handle recent messages after a short period offline or synchronize conversations across multiple devices.

At the end of the day, keeping your chat history is your responsibility, and this is a good place to apply a local-first approach.

Synchronisation

Seamless switching between clients is handled by XEP-0280: Message Carbons. Before its introduction, only incoming messages were synced between devices, while your own outgoing messages were not. Protocol-level mirroring of your own messages is a rather non-obvious feature :D

It's important to note that with end-to-end encryption, the concept of trusted fingerprints also applies to your own clients. For seamless synchronisation of outgoing messages, all your clients must trust each other's fingerprints. A new client, or an old one that was not trusted at the time messages were sent, will receive the full history from MAM but will not be able to decrypt it.
Yes, even your own messages.

In theory, re-encrypting messages on already trusted clients could solve this issue, but no XMPP client implements it yet. So in practice you may need to manually resend some data to a new device.

Message Correction

Its worth keeping in mind that features that seem simple and straightforward at first glance, such as message editing and deletion, actually rely on client-side implementation and may not behave for your recipient the way you expect.

Theyre fine to use and are well supported in some clients, but you shouldnt rely on them to hide anything.

Maintenance

OMEMO was designed as a set-it-and-forget-it solution, and it mostly succeeds in that goal. If you have a basic understanding of how the protocol works and check in online from time to time, there shouldnt be any surprises.

All maintenance comes down to making regular backups and notifying your contacts when fingerprints are added or no longer valid so they can keep their trust list up to date.

Step-by-step Guide

Lets say I have a XMPP account, me@some.server, and a few devices: a phone, a laptop, and a desktop computer. First Ill describe my mindset at a high level, then Ill add some notes about specific clients.

Client Roles

On the one hand, I have my phone. Its almost always with me and almost always online. Thats where I keep the full chat history and get real-time notifications.

On the other hand, I have a couple of desktop applications. I only open them when I need to discuss something using my keyboard or move some text between devices. I like to think of them as ad-hoc or satellite clients.

Before the Start

First, enable OMEMO encryption on every client if it isn't enabled by default. The client will usually generate the keys and fingerprint automatically.

The next step is to add all clients to the trust list on each device: my phone should trust all my computers, and my computers should trust each other as well as my phone.

Fingerprints do not have to be secret, so they can be published on your website or even on social media profiles. Here is my page with the fingerprints: https://oddsquat.org/about/keys/

Start the Conversation in Person

Lets say I meet Alice, we start talking, and decide to continue the conversation online.

I open a special QR code on my phone, and Alice scans it with her client. This QR code already contains the fingerprints of all my devices, so no extra steps are needed.

Then I do the same and scan the QR code from Alices screen with my mobile client.

Now we are both sure that its really us in the conversation, and that all messages will be available on all our devices and only on them.

Start the Conversation Online

Lets say Bob and I start discussing something on a forum or in the Fediverse, and then decide to move to XMPP.

Bob starts the chat. I trust the first device he messages me from, and then we exchange fingerprints for our other devices, if we have any. This approach is called TOFU (Trust On First Use).

Bob can confirm its really me using my page with fingerprints. I can confirm its really him by asking him to send his fingerprints in a private message on the same forum or via email.

Ideally, Bob also has a public page with his fingerprints. That way, we can both independently verify that we are who we say we are.

New or Lost Devices

If I start using a new device or install another client application, the first thing I do is add it to the list of trusted clients on my existing devices.

If I lose one of my devices or delete any private keys, the first thing I do is remove the corresponding client from the trusted list on my other devices.

Once Ive updated all my personal lists, I should inform my contacts about changes via trusted channels.

I can simply ask Alice to scan my new QR code the next time we meet, and send Bob a message introducing my new client or letting him know that the lost device is no longer trusted and that no real messages will ever come from it again.

Client Applications

This section describes how OMEMO is used in specific client applications that I personally use.

Conversations and Forks

Conversations is a modern, fully featured chat application for Android. It supports everything a messaging app should support: chats, voice calls, and sharing photos and files.

There are several forks of it where the UI or UX may differ, but the core features work exactly the same. I personally use Monocles Chat.

On the Contact Details screen (including your own account), you can see a list of published fingerprints and manually mark them as trusted or revoke trust.

To simplify all these routine operations, a QR-code-based system is used: You can show your own QR code or scan other peoples codes directly from the main screen. This makes device verification during in-person meetings simple and effortless.

Dino

Dino is a lightweight GTK-based GUI client.

It can be considered a fully functional one, although some non-essential features are still not supported or implemented. For example, it is not possible to clear local chat history using built-in methods :D

Trust and untrust decisions can be easily managed in the Encryption tab of the Conversation Details window.

It is important to note that, by default, Dino is configured to automatically trust new fingerprints. I recommend disabling this feature.

Profanity

Это могучий TUI-клиент, где всё-всё-всё реализовано через встроенную систему команд.

Если вы зачем-то намерены им пользоваться, то ниже вас ожидает небольшой читшит по использованию OMEMO, но я настойчиво рекомендую ознакомиться с полной документацией самостоятельно.

  • Генерация ключа и добавление своих устройств:

    /omemo gen
    /omemo trust me@some.server some-cool-fingerprint-01
    /omemo trust me@some.server another-cool-fingerprint
    /omemo qrcode
    
  • Увидеть список своих или чужих fingerprint'ов:

    /omemo fingerprint me@some.server
    /omemo fingerprint alice@another.server
    

    Доверенные будут помечены как trusted.

  • Начать зашифрованный диалог:

    /omemo start alice@another.server
    
  • Добавить чужой фингерпринт в список доверенных:

    /omemo trust alice@another.server some-cool-fingerprint-02
    /omemo trust alice@another.server some-cool-fingerprint-03
    /omemo trust bob@another.server some-cool-fingerprint-04
    
  • Перестать доверять кокретному клиенту:

    /omemo untrust alice@another.server some-cool-fingerprint-02