oddsquat/docs/posts/2026/encrypted_XMPP/index.html
2026-04-23 19:15:50 +02:00

428 lines
19 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta
name="viewport"
content="width=device-width, initial-scale=1.0">
<link rel="icon" href="/favicon.ico" sizes="32x32">
<link rel="icon" href="/icon.svg" type="image/svg+xml">
<title>
encrypted XMPP | oddsquat
</title>
<meta name="description" content="Secure and private messaging with XMPP and OMEMO encryption.">
<link rel="preload" href="/fonts/open_sans_condensed-32.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/fonts/open_sans_condensed-27.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/fonts/open_sans-25.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/fonts/open_sans-24.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/fonts/open_sans-17.woff2" as="font" type="font/woff2" crossorigin>
<link rel="stylesheet" type="text/css" href="/css/fonts.css">
<link rel="stylesheet" type="text/css" href="/css/typography.css">
<link rel="stylesheet" type="text/css" href="/css/main.css">
</head>
<body>
<div class="stripesContainer">
<div class="stripes">
</div>
</div>
<header>
<nav>
<ul>
<li><a href="/">oddsquat</a></li>
<li><a href="/posts/">
posts</a></li>
<li><a href="/posts/#2026">
2026</a></li>
<li>encrypted XMPP</li>
</ul>
</nav>
</header>
<main>
<article>
<h1 id="end-to-end-encryption-inxmpp-with-omemo">End-to-End Encryption in&nbsp;XMPP with OMEMO</h1>
<p>I&nbsp;find it&nbsp;funny that twenty years ago I&nbsp;was already trying
to&nbsp;get people to&nbsp;switch to&nbsp;XMPP.</p>
<p>For a&nbsp;long time, ICQ was extremely popular around me,
but the proprietary messenger kept breaking things for people
using alternative clients, which was quite annoying.
After yet another round of&nbsp;this pointless battle
I&nbsp;realized clearly that I&nbsp;prefer protocols over services.</p>
<p>I&nbsp;didnt have much success back then,
but fortunately, XMPP (and I&nbsp;hope I&nbsp;have too)
has continued moving forward over the past two decades.
It&nbsp;has developed slowly, sometimes awkwardly, but steadily.</p>
<p>Here, I&nbsp;wont talk about why XMPP is&nbsp;great or&nbsp;how it&nbsp;works.
You can check
<a
href='https://contrapunctus.codeberg.page/the-quick-and-easy-guide-to-xmpp.html'
target='_blank'>
this guide</a>
(one of&nbsp;many) and Id rather not write another one.
In&nbsp;this post,
I&nbsp;want to&nbsp;focus specifically on&nbsp;end-to-end encryption
and the practical aspects of&nbsp;using it.</p>
<h2 id="short-glossary">Short Glossary</h2>
<p><strong>End-to-end encryption</strong> is&nbsp;a&nbsp;way
to&nbsp;keep your chats truly private.<br>
Only you and the person youre messaging can read the messages.
Not even the server owner has the keys
needed to&nbsp;decrypt or&nbsp;modify them.</p>
<p><strong>XMPP</strong> is&nbsp;an&nbsp;extensible protocol for instant messaging.
Its open, decentralized, and mature.</p>
<p><strong>OMEMO</strong> is&nbsp;a&nbsp;<a
href='https://omemo.top/'
target='_blank'>widely supported</a>
XMPP Extension Protocol (XEP)
for secure multi-client end-to-end encryption.
You can read more about
it&nbsp;on&nbsp;a&nbsp;<a
href='https://conversations.im/omemo/'
target='_blank'>dedicated page by&nbsp;Daniel Gultsch</a>.</p>
<p><strong>Client</strong>, in&nbsp;this post,
means a&nbsp;specific instance
of&nbsp;an&nbsp;XMPP application on&nbsp;a&nbsp;user device.
<br>OMEMO-related documentation uses the term Device,
but I&nbsp;find it&nbsp;potentially confusing:
in&nbsp;practice, a&nbsp;single physical device
can run multiple independent clients.</p>
<h2 id="basic-concepts">Basic Concepts</h2>
<p>This section introduces some basics of&nbsp;end-to-end encryption.</p>
<p>If&nbsp;youre already familiar with the concepts and terminology,
you can skip ahead to&nbsp;<a
href='#practical-aspects-of-omemo-and-xmpp'>how end-to-end encryption
affects the XMPP user experience</a>,
or&nbsp;jump straight to&nbsp;the <a
href='#step-by-step-guide'>step-by-step workflow</a> I&nbsp;personally use.</p>
<h3 id="trade-offs-between-safety-and-convenience">Trade-offs Between Safety and Convenience</h3>
<p>Unfortunately, things that are truly secure are rarely convenient.
They often require some initial efforts
and a&nbsp;bit of&nbsp;ongoing attention.</p>
<p>Telegram, which used to&nbsp;be&nbsp;a&nbsp;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&nbsp;full set of&nbsp;limitations:
theyre one-on-one only,
cant be&nbsp;synced to&nbsp;another device,
arent available on&nbsp;desktop at&nbsp;all,
and so&nbsp;on.</p>
<p>All commercial so-called “secure” messengers, like Signal or&nbsp;WhatsApp,
end up&nbsp;with pretty similar limitations,
because its tricky to&nbsp;make end-to-end encrypted chats
work the way users expect.</p>
<p>Luckily, protocols and cryptography dont care about
convenience or&nbsp;user expectations.
Many XMPP clients let you do&nbsp;almost anything youre trying to&nbsp;do.
Sometimes its clunky and unintuitive,
sometimes its the kind of&nbsp;freedom
that lets you shoot yourself in&nbsp;the foot.
At&nbsp;the end of&nbsp;the day, youd better understand what youre doing.</p>
<p>It&nbsp;might sound messy, but for that price, XMPP actually
gives you a&nbsp;lot of&nbsp;handy features:
your chats are secured with Signal-grade end-to-end encryption,
and you can use as&nbsp;many devices as&nbsp;you want,
all at&nbsp;the same time,
without being tied to&nbsp;any proprietary service.</p>
<p>In&nbsp;general, the XMPP experience today
could be&nbsp;described as&nbsp;a&nbsp;“WhatsApp with benefits and frictions”.
Its kinda ironic, considering that WhatsApps protocol
is&nbsp;actually based on&nbsp;XMPP, but incompatibly altered and defederated.</p>
<h3 id="keys-fingerprints-and-trust">Keys, Fingerprints and Trust</h3>
<p>OMEMO is&nbsp;based on&nbsp;the <a
href='https://en.wikipedia.org/wiki/Double_Ratchet_Algorithm'
target='_blank'>
Double Ratchet Algorithm</a>.
While the internal details are quite interesting,
for practical purposes its enough to&nbsp;know that
each client stores some cryptographic keys
and can derive a&nbsp;human-readable hash from them,
commonly called a&nbsp;fingerprint.</p>
<p>Keys are usually managed automatically by&nbsp;the XMPP client,
and in&nbsp;normal use you should never need to&nbsp;handle them manually.
In&nbsp;fact, you probably dont even need to&nbsp;know what they look like.</p>
<p>A&nbsp;fingerprint lets you identify
a&nbsp;specific client of&nbsp;your contact
and verify that it&nbsp;hasnt been spoofed.
Fingerprints for an&nbsp;account are not secret:
clients publish their own fingerprints to&nbsp;the XMPP server
and automatically receive the fingerprints of&nbsp;others.
Only fingerprints you explicitly mark as&nbsp;trusted are relevant.</p>
<p>In&nbsp;an&nbsp;typical scenario, the contact should confirm in&nbsp;person
or&nbsp;through an&nbsp;already trusted and secure communication channel
that the fingerprint belongs to&nbsp;their device,
and only then you mark it&nbsp;as&nbsp;trusted.</p>
<p>The list of&nbsp;trusted fingerprints
is&nbsp;used at&nbsp;the moment a&nbsp;message is&nbsp;sent.
Behind the scenes,
OMEMO performs a&nbsp;certain amount of&nbsp;key management,
and only the clients that are present in&nbsp;the trusted list
at&nbsp;the time of&nbsp;encryption
will be&nbsp;able to&nbsp;decrypt the message later.</p>
<p>Its important to&nbsp;understand
that trust cannot be&nbsp;applied retroactively:
its not possible to&nbsp;“extend” trust to&nbsp;new clients
after a&nbsp;message has already been encrypted and sent.</p>
<h2 id='practical-aspects-of-omemo-and-xmpp'>
Practical Aspects of&nbsp;OMEMO and XMPP
</h2>
<h3 id="chat-history">Chat History</h3>
<p>In&nbsp;theory, XMPP supports server-side message history storage via
<strong>XEP-0313: Message Archive Management</strong>.</p>
<p>In&nbsp;practice, support for this XEP,
as&nbsp;well as&nbsp;retention policies and message lifetime,
depends on&nbsp;the specific server.
You should never assume that all conversations are stored
indefinitely by&nbsp;default.
From a&nbsp;practical standpoint,
the server-side MAM archive is&nbsp;better considered a&nbsp;cache:
it&nbsp;can help you handle recent messages after a&nbsp;short period offline
or&nbsp;synchronize conversations across multiple devices.</p>
<p>At&nbsp;the end of&nbsp;the day,
keeping your chat history is&nbsp;your responsibility,
and this is&nbsp;a&nbsp;good place to&nbsp;apply a&nbsp;local-first approach.</p>
<h3 id="synchronisation">Synchronisation</h3>
<p>Seamless switching between clients is&nbsp;handled by
<strong>XEP-0280: Message Carbons</strong>.
Before its introduction, only incoming messages were synced between devices,
while your own outgoing messages were not.
Protocol-level mirroring of&nbsp;your own messages
is&nbsp;a&nbsp;rather non-obvious feature :D</p>
<p>Its important to&nbsp;note that with end-to-end encryption,
the concept of&nbsp;trusted fingerprints also applies to&nbsp;your own clients.
For seamless synchronisation of&nbsp;outgoing messages,
all your clients must trust each others fingerprints.
A&nbsp;new client,
or&nbsp;an&nbsp;old one that was not trusted
at&nbsp;the time messages were sent,
will receive the full history from MAM
but will not be&nbsp;able to&nbsp;decrypt it.
<br>Yes, even your own messages.</p>
<p>In&nbsp;theory, re-encrypting messages on&nbsp;already trusted clients
could solve this issue, but no&nbsp;XMPP client implements it&nbsp;yet.
So&nbsp;in&nbsp;practice you may need to&nbsp;manually resend
some data to&nbsp;a&nbsp;new device.</p>
<h3 id="message-correction">Message Correction</h3>
<p>Its worth keeping in&nbsp;mind that
features that seem simple and straightforward at&nbsp;first glance,
such as&nbsp;message editing and deletion,
actually rely on&nbsp;client-side implementation
and may not behave for your recipient the way you expect.</p>
<p>Theyre fine to&nbsp;use and are well supported in&nbsp;some clients,
but you shouldnt rely on&nbsp;them to&nbsp;hide anything.</p>
<h3 id="maintenance">Maintenance</h3>
<p>OMEMO was designed as&nbsp;a&nbsp;set-it-and-forget-it solution
and mostly succeeds in&nbsp;that goal.
If&nbsp;you have a&nbsp;basic understanding of&nbsp;how the protocol works
and check in&nbsp;online from time to&nbsp;time,
there shouldnt be&nbsp;any surprises.</p>
<p>All maintenance comes down to&nbsp;making regular backups
and notifying your contacts
when fingerprints are added or&nbsp;no&nbsp;longer valid
so&nbsp;they can keep their trust list up&nbsp;to&nbsp;date.</p>
<h2 id="step-by-step-guide">Step-by-Step Guide</h2>
<p>Lets say I&nbsp;have a&nbsp;XMPP account, <code>me@some.server</code>,
and a&nbsp;few devices:
a&nbsp;phone, a&nbsp;laptop, and a&nbsp;desktop computer.
First Ill describe my&nbsp;mindset at&nbsp;a&nbsp;high level,
then Ill add some notes about specific clients.</p>
<h3 id="client-roles">Client Roles</h3>
<p>On&nbsp;the one hand, I&nbsp;have my&nbsp;phone.
Its almost always with me&nbsp;and almost always online.
Thats where I&nbsp;keep the full chat history
and get real-time notifications.</p>
<p>On&nbsp;the other hand, I&nbsp;have a&nbsp;couple of&nbsp;desktop applications.
I&nbsp;only open them
when I&nbsp;need to&nbsp;discuss something using my&nbsp;keyboard
or&nbsp;share some text between devices.
I&nbsp;like to&nbsp;think of&nbsp;them as&nbsp;satellite clients.</p>
<h3 id="before-the-start">Before the Start</h3>
<p>First, enable OMEMO encryption
on&nbsp;every client if&nbsp;it&nbsp;isnt enabled by&nbsp;default.</p>
<p>The next step is&nbsp;to&nbsp;add
all clients to&nbsp;the trust list on&nbsp;each device:
my&nbsp;phone should trust all my&nbsp;computers,
and my&nbsp;computers should trust each other
as&nbsp;well as&nbsp;my&nbsp;phone.</p>
<p>Fingerprints do&nbsp;not have to&nbsp;be&nbsp;secret,
so&nbsp;they can be&nbsp;published on
your website or&nbsp;even on&nbsp;social media profiles.
Here is&nbsp;my&nbsp;page with the fingerprints, for example:
<br><a href='https://oddsquat.org/about/keys/' target='_blank'>
https://oddsquat.org/about/keys/
</a></p>
<h3 id="start-the-conversation-inperson">Start the Conversation in&nbsp;Person</h3>
<p>Lets say I&nbsp;meet Alice,
we&nbsp;start talking,
and then decide to&nbsp;continue the conversation online.</p>
<p>I&nbsp;open a&nbsp;special QR&nbsp;code on&nbsp;my&nbsp;phone,
and Alice scans it&nbsp;with her client.
This QR&nbsp;code already contains
the fingerprints of&nbsp;all my&nbsp;devices,
so&nbsp;no&nbsp;extra steps are needed on&nbsp;her phone.
After that, I&nbsp;do&nbsp;the same
and scan her QR&nbsp;code as&nbsp;well.</p>
<p>Later at&nbsp;home,
I&nbsp;manually mark her devices as&nbsp;trusted on&nbsp;my&nbsp;computers
using the trusted list on&nbsp;my&nbsp;phone, and she does the same.</p>
<p>Now we&nbsp;are both sure
that it&nbsp;is&nbsp;really us&nbsp;in&nbsp;the conversation,
and that all messages will be&nbsp;available
on&nbsp;all our devices and only on&nbsp;them.</p>
<h3 id="start-the-conversation-online">Start the Conversation Online</h3>
<p>Lets say Bob and I&nbsp;start discussing something
on&nbsp;a&nbsp;forum or&nbsp;in&nbsp;the Fediverse,
and then decide to&nbsp;continue the discussion on&nbsp;XMPP.</p>
<p>Before starting the chat,
Bob can confirm its really me&nbsp;using my&nbsp;page with fingerprints.
I&nbsp;can confirm its really him
by&nbsp;asking him to&nbsp;send his fingerprints
in&nbsp;a&nbsp;private message on&nbsp;the same forum or&nbsp;via email.</p>
<p>Ideally, Bob also has a&nbsp;public page with his fingerprints.
That way, we&nbsp;can both independently verify
that we&nbsp;are who we&nbsp;say we&nbsp;are.</p>
<p>In&nbsp;an&nbsp;alternative scenario,
where there has been no&nbsp;prior communication or&nbsp;public pages
and only a&nbsp;single JID&nbsp;is known,
things play out a&nbsp;bit differently:
Bob starts the chat,
I&nbsp;trust the first device he&nbsp;messages me&nbsp;from,
and then we&nbsp;exchange fingerprints for our other devices,
if&nbsp;we&nbsp;have any.
This approach is&nbsp;called TOFU (Trust On&nbsp;First Use).</p>
<h3 id="new-orlost-devices">New or&nbsp;Lost Devices</h3>
<p>If&nbsp;I&nbsp;start using a&nbsp;new device
or&nbsp;install another client application,
the first thing I&nbsp;do&nbsp;is&nbsp;add it&nbsp;to&nbsp;the list
of&nbsp;trusted clients on&nbsp;my&nbsp;existing devices.</p>
<p>If&nbsp;I&nbsp;lose one of&nbsp;my&nbsp;devices
or&nbsp;delete any private keys,
the first thing I&nbsp;do&nbsp;is&nbsp;remove the corresponding client
from the trusted list on&nbsp;my&nbsp;other devices.</p>
<p>Once Ive updated all my&nbsp;personal lists,
I&nbsp;should inform my&nbsp;contacts about changes via trusted channels.</p>
<p>I&nbsp;can simply ask Alice to&nbsp;scan
my&nbsp;new QR&nbsp;code the next time we&nbsp;meet,
and send Bob a&nbsp;message introducing
my&nbsp;new client or&nbsp;letting him know
that the lost device is&nbsp;no&nbsp;longer trusted
and that no&nbsp;real messages will ever come from it&nbsp;again.</p>
<h2 id="client-applications">Client Applications</h2>
<p>This section describes
how OMEMO is&nbsp;used in&nbsp;specific client applications
that I&nbsp;personally use.</p>
<h3 id="conversations-and-forks">Conversations and Forks</h3>
<p><a
href='https://conversations.im/'
target='_blank'>
Conversations</a> is&nbsp;a&nbsp;modern,
fully featured chat application for Android.
It&nbsp;supports everything a&nbsp;messaging app should support:
chats, voice calls, video calls, and sharing files of&nbsp;any kind.</p>
<p>There are several forks of&nbsp;it&nbsp;where
the UI&nbsp;or&nbsp;UX&nbsp;may differ,
but the core features work exactly the same.
I&nbsp;personally use <a
href='https://codeberg.org/monocles/monocles_chat'
target='_blank'>
Monocles Chat</a>.</p>
<p>On&nbsp;the Contact Details screen (including your own account),
you can see a&nbsp;list of&nbsp;published fingerprints
and manually mark them as&nbsp;trusted or&nbsp;revoke trust.</p>
<p>To&nbsp;simplify all these routine operations,
a&nbsp;QR-code-based system is&nbsp;used:
you can show your own QR&nbsp;code or&nbsp;scan other peoples codes
directly from the main screen.
This makes device verification during in-person meetings
simple and effortless.</p>
<h3 id="dino">Dino</h3>
<p><a
href='https://dino.im/'
target='_blank'>
Dino</a> is&nbsp;a&nbsp;lightweight GTK-based GUI client.</p>
<p>It&nbsp;can be&nbsp;considered a&nbsp;fully functional one,
although some non-essential features are still not implemented.
For example,
it&nbsp;is&nbsp;not possible to&nbsp;clear local chat history
using built-in methods :D</p>
<p>Trust and untrust decisions can be&nbsp;easily managed
in&nbsp;the Encryption tab of&nbsp;the Conversation Details window.</p>
<p>It&nbsp;is&nbsp;important to&nbsp;note that,
by&nbsp;default, Dino is&nbsp;configured
to&nbsp;automatically trust new fingerprints.
I&nbsp;recommend disabling this feature.</p>
<h3 id="profanity">Profanity</h3>
<p><a
href='https://profanity-im.github.io/'
target='_blank'>
Profanity</a> is&nbsp;a&nbsp;powerful TUI client
where everything is&nbsp;controlled through a&nbsp;built-in command system.</p>
<p>If&nbsp;you somehow intend to&nbsp;use it,
you can find a&nbsp;small cheat sheet for the <code>omemo</code> command below.
However, I&nbsp;strongly recommend reading the full documentation.</p>
<ul>
<li><p>Generate a&nbsp;key and add your other clients:</p>
<pre><code class="language-text">/omemo gen
/omemo trust me@some.server some-cool-fingerprint-01
/omemo trust me@some.server another-cool-fingerprint
/omemo qrcode</code></pre>
</li>
<li><p>View the list of&nbsp;your own or&nbsp;someone elses fingerprints:</p>
<pre><code class="language-text">/omemo fingerprint me@some.server
/omemo fingerprint alice@another.server</code></pre>
<p>Trusted ones will be&nbsp;marked as&nbsp;<code>trusted</code>.</p>
</li>
<li><p>Start an&nbsp;encrypted conversation:</p>
<pre><code class="language-text">/omemo start alice@another.server</code></pre>
</li>
<li><p>Add fingerprints to&nbsp;the trusted list:</p>
<pre><code class="language-text">/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</code></pre>
</li>
<li><p>Revoke trust for a&nbsp;specific client:</p>
<pre><code class="language-text">/omemo untrust alice@another.server some-cool-fingerprint-02</code></pre>
</li>
</ul>
<h2 id="late-disclaimer">Late Disclaimer</h2>
<p>This post was originally intended
as&nbsp;a&nbsp;collection of&nbsp;answers to&nbsp;questions
I&nbsp;had when I&nbsp;first started using XMPP with OMEMO.</p>
<p>It&nbsp;isnt meant to&nbsp;be&nbsp;exhaustive or&nbsp;formal,
but rather to&nbsp;clarify the practical side of&nbsp;things
and reduce that initial feeling of&nbsp;being lost
when you keep running into
“The message was not encrypted for this device”
over and over again.</p>
<p>From now on, I&nbsp;hope you wont encounter such errors
or&nbsp;any other issues with end-to-end encryption,
and youll feel confident using it&nbsp;in&nbsp;XMPP.</p>
</article>
</main>
<footer>
2026-04-23
</footer>
<script async
data-goatcounter="https://he4et.goatcounter.com/count"
src="https://gc.zgo.at/count.js"></script>
</body>
</html>