mirror of
https://github.com/He4eT/oddsquat.git
synced 2026-05-05 04:47:25 +00:00
427 lines
19 KiB
HTML
427 lines
19 KiB
HTML
<!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 XMPP with OMEMO</h1>
|
||
<p>I find it funny that twenty years ago I was already trying
|
||
to get people to switch to XMPP.</p>
|
||
<p>For a 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 this pointless battle
|
||
I realized clearly that I prefer protocols over services.</p>
|
||
<p>I didn’t 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.</p>
|
||
<p>Here, I won’t talk about why XMPP is great or how it 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 many) and I’d 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.</p>
|
||
<h2 id="short-glossary">Short Glossary</h2>
|
||
<p><strong>End-to-end encryption</strong> is a way
|
||
to keep your chats truly private.<br>
|
||
Only you and the person you’re messaging can read the messages.
|
||
Not even the server owner has the keys
|
||
needed to decrypt or modify them.</p>
|
||
<p><strong>XMPP</strong> is an extensible protocol for instant messaging.
|
||
It’s open, decentralized, and mature.</p>
|
||
<p><strong>OMEMO</strong> is a <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 on a <a
|
||
href='https://conversations.im/omemo/'
|
||
target='_blank'>dedicated page by Daniel Gultsch</a>.</p>
|
||
<p><strong>Client</strong>, in this post,
|
||
means a specific instance
|
||
of an XMPP application on a user device.
|
||
<br>OMEMO-related documentation uses the term Device,
|
||
but I find it potentially confusing:
|
||
in practice, a single physical device
|
||
can run multiple independent clients.</p>
|
||
<h2 id="basic-concepts">Basic Concepts</h2>
|
||
<p>This section introduces some basics of end-to-end encryption.</p>
|
||
<p>If you’re already familiar with the concepts and terminology,
|
||
you can skip ahead to <a
|
||
href='#practical-aspects-of-omemo-and-xmpp'>how end-to-end encryption
|
||
affects the XMPP user experience</a>,
|
||
or jump straight to the <a
|
||
href='#step-by-step-guide'>step-by-step workflow</a> I 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 bit of ongoing attention.</p>
|
||
<p>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:
|
||
they’re one-on-one only,
|
||
can’t be synced to another device,
|
||
aren’t available on desktop at all,
|
||
and so on.</p>
|
||
<p>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.</p>
|
||
<p>Luckily, protocols and cryptography don’t care about
|
||
convenience or user expectations.
|
||
Many XMPP clients let you do almost anything you’re trying to do.
|
||
Sometimes it’s clunky and unintuitive,
|
||
sometimes it’s the kind of freedom
|
||
that lets you shoot yourself in the foot.
|
||
At the end of the day, you’d better understand what you’re doing.</p>
|
||
<p>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.</p>
|
||
<p>In general, the XMPP experience today
|
||
could be described as a “WhatsApp with benefits and frictions”.
|
||
It’s kinda ironic, considering that WhatsApp’s protocol
|
||
is actually based on XMPP, but incompatibly altered and defederated.</p>
|
||
<h3 id="keys-fingerprints-and-trust">Keys, Fingerprints and Trust</h3>
|
||
<p>OMEMO is based on 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 it’s enough to know that
|
||
each client stores some cryptographic keys
|
||
and can derive a hash from them, commonly called a fingerprint.</p>
|
||
<p>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 don’t even need to know what they look like.</p>
|
||
<p>A fingerprint lets you identify
|
||
a specific client of your contact
|
||
and verify that it hasn’t 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.</p>
|
||
<p>In an typical 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.</p>
|
||
<p>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.</p>
|
||
<p>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.</p>
|
||
<h2 id='practical-aspects-of-omemo-and-xmpp'>
|
||
Practical Aspects of OMEMO and XMPP
|
||
</h2>
|
||
|
||
<h3 id="chat-history">Chat History</h3>
|
||
<p>In theory, XMPP supports server-side message history storage via
|
||
<strong>XEP-0313: Message Archive Management</strong>.</p>
|
||
<p>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.</p>
|
||
<p>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.</p>
|
||
<h3 id="synchronisation">Synchronisation</h3>
|
||
<p>Seamless switching between clients is 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 your own messages
|
||
is a rather non-obvious feature :D</p>
|
||
<p>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.
|
||
<br>Yes, even your own messages.</p>
|
||
<p>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.</p>
|
||
<h3 id="message-correction">Message Correction</h3>
|
||
<p>It’s 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.</p>
|
||
<p>They’re fine to use and are well supported in some clients,
|
||
but you shouldn’t rely on them to hide anything.</p>
|
||
<h3 id="maintenance">Maintenance</h3>
|
||
<p>OMEMO was designed as a set-it-and-forget-it solution
|
||
and 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 shouldn’t be any surprises.</p>
|
||
<p>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.</p>
|
||
<h2 id="step-by-step-guide">Step-by-Step Guide</h2>
|
||
<p>Let’s say I have a XMPP account, <code>me@some.server</code>,
|
||
and a few devices:
|
||
a phone, a laptop, and a desktop computer.
|
||
First I’ll describe my mindset at a high level,
|
||
then I’ll add some notes about specific clients.</p>
|
||
<h3 id="client-roles">Client Roles</h3>
|
||
<p>On the one hand, I have my phone.
|
||
It’s almost always with me and almost always online.
|
||
That’s where I keep the full chat history
|
||
and get real-time notifications.</p>
|
||
<p>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 share some text between devices.
|
||
I like to think of them as satellite clients.</p>
|
||
<h3 id="before-the-start">Before the Start</h3>
|
||
<p>First, enable OMEMO encryption
|
||
on every client if it isn’t enabled by default.</p>
|
||
<p>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.</p>
|
||
<p>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, 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 Person</h3>
|
||
<p>Let’s say I meet Alice,
|
||
we start talking,
|
||
and then decide to continue the conversation online.</p>
|
||
<p>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 on her phone.
|
||
After that, I do the same
|
||
and scan her QR code as well.</p>
|
||
<p>Later at home,
|
||
I manually mark her devices as trusted on my computers
|
||
using the trusted list on my phone, and she does the same.</p>
|
||
<p>Now we are both sure
|
||
that it is really us in the conversation,
|
||
and that all messages will be available
|
||
on all our devices and only on them.</p>
|
||
<h3 id="start-the-conversation-online">Start the Conversation Online</h3>
|
||
<p>Let’s say Bob and I start discussing something
|
||
on a forum or in the Fediverse,
|
||
and then decide to continue the discussion on XMPP.</p>
|
||
<p>Before starting the chat,
|
||
Bob can confirm it’s really me using my page with fingerprints.
|
||
I can confirm it’s really him
|
||
by asking him to send his fingerprints
|
||
in a private message on the same forum or via email.</p>
|
||
<p>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.</p>
|
||
<p>In an alternative scenario,
|
||
where there has been no prior communication or public pages
|
||
and only a single JID is known,
|
||
things play out a bit differently:
|
||
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).</p>
|
||
<h3 id="new-orlost-devices">New or Lost Devices</h3>
|
||
<p>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.</p>
|
||
<p>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.</p>
|
||
<p>Once I’ve updated all my personal lists,
|
||
I should inform my contacts about changes via trusted channels.</p>
|
||
<p>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.</p>
|
||
<h2 id="client-applications">Client Applications</h2>
|
||
<p>This section describes
|
||
how OMEMO is used in specific client applications
|
||
that I personally use.</p>
|
||
<h3 id="conversations-and-forks">Conversations and Forks</h3>
|
||
<p><a
|
||
href='https://conversations.im/'
|
||
target='_blank'>
|
||
Conversations</a> is a modern,
|
||
fully featured chat application for Android.
|
||
It supports everything a messaging app should support:
|
||
chats, voice calls, video calls, and sharing files of any kind.</p>
|
||
<p>There are several forks of it where
|
||
the UI or UX may differ,
|
||
but the core features work exactly the same.
|
||
I personally use <a
|
||
href='https://codeberg.org/monocles/monocles_chat'
|
||
target='_blank'>
|
||
Monocles Chat</a>.</p>
|
||
<p>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.</p>
|
||
<p>To simplify all these routine operations,
|
||
a QR-code-based system is used:
|
||
you can show your own QR code or scan other people’s 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 a lightweight GTK-based GUI client.</p>
|
||
<p>It can be considered a fully functional one,
|
||
although some non-essential features are still not implemented.
|
||
For example,
|
||
it is not possible to clear local chat history
|
||
using built-in methods :D</p>
|
||
<p>Trust and untrust decisions can be easily managed
|
||
in the Encryption tab of the Conversation Details window.</p>
|
||
<p>It is important to note that,
|
||
by default, Dino is configured
|
||
to automatically trust new fingerprints.
|
||
I recommend disabling this feature.</p>
|
||
<h3 id="profanity">Profanity</h3>
|
||
<p><a
|
||
href='https://profanity-im.github.io/'
|
||
target='_blank'>
|
||
Profanity</a> is a powerful TUI client
|
||
where everything is controlled through a built-in command system.</p>
|
||
<p>If you somehow intend to use it,
|
||
you can find a small cheat sheet for the <code>omemo</code> command below.
|
||
However, I strongly recommend reading the full documentation.</p>
|
||
<ul>
|
||
<li><p>Generate a 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 your own or someone else’s fingerprints:</p>
|
||
<pre><code class="language-text">/omemo fingerprint me@some.server
|
||
/omemo fingerprint alice@another.server</code></pre>
|
||
<p>Trusted ones will be marked as <code>trusted</code>.</p>
|
||
</li>
|
||
<li><p>Start an encrypted conversation:</p>
|
||
<pre><code class="language-text">/omemo start alice@another.server</code></pre>
|
||
</li>
|
||
<li><p>Add fingerprints to 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 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 a collection of answers to questions
|
||
I had when I first started using XMPP with OMEMO.</p>
|
||
<p>It isn’t meant to be exhaustive or formal,
|
||
but rather to clarify the practical side of things
|
||
and reduce that initial feeling of 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 hope you won’t encounter such errors
|
||
or any other issues
|
||
connected to end-to-end encryption in XMPP.</p>
|
||
|
||
</article>
|
||
</main>
|
||
|
||
<footer>
|
||
2026-04-16
|
||
</footer>
|
||
|
||
<script async
|
||
data-goatcounter="https://he4et.goatcounter.com/count"
|
||
src="https://gc.zgo.at/count.js"></script>
|
||
|
||
|
||
</body>
|
||
</html>
|