Categories
Cryptography Open Source Software Security

Against XMPP+OMEMO

XMPP is a messaging protocol (among other things) that needs no introduction to any technical audience. Its various implementations have proliferated through technical communities for decades.

Many large tech companies today used to run XMPP servers. However, the basic protocol transmitted plaintext. If you were lucky, it was plaintext over SSL/TLS, but servers could still see all of your message contents.

OMEMO (XEP-0384) is an attempt to staple end-to-end encryption onto the XMPP ecosystem.

It’s largely inspired by, and based on, an earlier version of the Signal protocol, so you might be tempted to believing that it’s good.

In my opinion, it’s not.

OMEMO is not the worst attempt at making XMPP encrypted (see: XEP-0027 for that), but it still doesn’t meet the bar for the kind of private messaging app that Signal is, and is not a viable competitor to Signal.

To understand why this is true, you only need check whether OMEMO is on by default (it isn’t), or whether OMEMO can be turned off even if your client supports it (it can).

Both of these conditions fail the requirements I outlined under the End-to-End Encryption header in that other blog post.

And that’s all that I should have needed to say on the matter.

Art: Harubaki

Unfortunately, the Internet is full of cargo cults built around specific technologies, and their followers have an emotional interest in muddying the waters.

Criticize one, and their rivals will come out of the woodwork to say, “This is why I use $FooBar instead of $BazQuux!” and insist that their preferred solution is the secure one–with all the confident incorrectness of a climate change denier.

Art: AJ

Let me explain why I don’t recommend XMPP and OMEMO for private messaging.

But before I do, a quick reminder that me criticizing XMPP+OMEMO isn’t an endorsement of weird or stupid alternatives, like using PGP.

Also, this post is about the entire XMPP + OMEMO ecosystem, not just the OMEMO specification.

Why People Like XMPP

Quite simply, people like XMPP because it’s federated, which means they can control who gets access to their data. This is also one reason why people like Matrix, Mastodon, NextCloud, etc. It gives them a sense of control over their data that a centralized platform doesn’t. You can feel like you’re controlling your own destiny, whether or not that’s true. And those emotions can be a powerful thing.

Unlike Moxie, I don’t inherently think federated technology is always bad.

There are mechanisms you can employ to roll the entire ecosystem forward and keep everyone up-to-date with security fixes to an underlying protocol. Being frozen in time is a real problem in federation, but not an inevitability.

Unfortunately, XMPP is the perfect exhibit for anyone that wants to argue in favor of Moxie’s perspective on federation.

OMEMO Problem 1: Protocol Freeze

When I decided to set out to survey the XMPP+OMEMO ecosystem, the first question that came to mind is, “Which implementation is everyone using?”

The answer is probably Conversations. Other answers I heard were Gajim and forks of Conversations.

We’ll get back to Conversations later, but right now, I want to address a bigger problem with the XMPP+OMEMO ecosystem.

The latest draft of XEP-0384 is version 0.8.3, published in January 2022.

Despite this, almost every OMEMO implementation I can find is still on version 0.3.0 (or earlier) of the OMEMO specification.

Drakeposting No Sticker
Art: CMYKat

The easiest way to tell if they aren’t implementing version 0.4.0 or newer is the absence of AES-256-CBC in their codebase.

See for yourself. All of the following implement version 0.3.0 or older of the OMEMO specification:

The only implementations I found that supported newer protocol versions were Profanity and Kaidan (via QXmpp).

EDIT: Thanks to tant for pointing me to this list of versions.

What To Even Focus On?

A question comes to mind: Which version of the specification should an enterprising security researcher look at?

Contemplating, Thinking Sticker
Art: CMYKat

The latest version available online, or an ancient version that’s still widely used?

If I find a problem in the latest draft of the specification, some will say that’s a non-issue because the spec is “experimental” and therefore should not implemented yet.

If I find a problem in the older draft of the specification, some will insist that those are known problems with an older version of the spec, and that people “should” be on the newer version if they expect to be secure.

Even worse, there’s probably some overlap between the two sets of people.

Regardless, anyone who’s vested in improving the security of the XMPP+OMEMO ecosystem is at an impasse right out of the gate. That’s not a good place to be in.

OMEMO Problem 2: YOLO Crypto

OMEMO doesn’t attempt to provide even the vaguest rationale for its design choices, and appears to approach cryptography protocol specification with a care-free attitude.

To put it mildly, this is the wrong way to approach cryptography. This is best explained with a concrete example.

Version 0.3.0 of XEP-0384 used AES-128-GCM to encrypt messages. (As we saw in the previous section, this is the version almost everyone is actually using.)

Version 0.4.0 was updated to use AES-256-CBC + HMAC-SHA-256 (Encrypt then HMAC), as Signal does.

Version 0.7.0 introduced yet another protocol change: The HMAC-SHA-256 authentication tag is now truncated to 128 bits.

And what was the changelog message for version 0.7.0?

Various fixes, clarifications and general improvements.

CHANGELOG, XEP-0384 0.7.0

You’ve got to be fucking kidding me.

So here’s the thing: These protocols all provide different security properties, and some of these differences are subtle. Switching from one to the other is the sort of change that should be accompanied by a clear reason.

See, for example, the PASETO v3/v4 rationale doc. That is the level of detail I’d want to see in OMEMO’s specification, especially the changelog. OMEMO’s rationale isn’t merely inadequate, it’s totally absent!

Technical Correction (2024-08-06)

A few people (or maybe the same person under different alts? Didn’t check, don’t really care) have pointed out that this section is not technically correct.

Apparently, while the actual steps in the encryption and decryption algorithms didn’t mention truncation at all, this was alluded to earlier in an earlier section.

I’m not going to significantly alter what I originally wrote above, because the earlier versions of the spec were poorly written and the instructions where absent from the section they needed to be in.

OMEMO Encryption Through the Ages

Before Version 0.4.0

AES-128-GCM doesn’t commit to the key, which can lead to an attack that we call “Invisible Salamanders”.

Historically, this was exploited in “abuse reporting” scenarios, but as I explained in my Threema disclosures, it can sometimes come up in group messaging scenarios.

For flavor, I wrote a GCM exploit in PHP for part of the DEFCON Furs’ badge challenge one year. It’s not a difficult one to pull off.

But if you’re in a setup where you only have one valid key for a given ciphertext, that’s not really a problem.

A bigger concern with this algorithm is that you’re constrained to 96-bit nonces, so if you’re using the same key for multiple messages, you have to worry about symmetric wear-out.

That being said, we can kind of summarize this as a short, reusable list.

  • Key entropy: 128 bits
  • Key commitment security: None
  • Safety limit: 2^{32} messages, each with a max length of 2^{36} - 32 bytes.
  • Authentication tag length: 128 bits
  • Authentication security: 128 bits (polynomial MAC)

Version 0.4.0 – 0.6.0

As mentioned previously, version 0.4.0 of the OMEMO specification moved towards AES-256-CBC + HMAC-SHA-256 for Stanza Content Encryption.

This is congruent with what Signal does, so I won’t belabor the point.

If you implement the encryption algorithm faithfully from the specification’s order of operations, truncation is omitted. However, it does get a passing mention in the double ratchet section, so the authors can argue it was intended “all along”.

  • Key entropy: 256 bits
  • Key commitment security: 128 bits 64 bits
  • Safety limit: It’s complicated. It can range from 2^{47} to 2^{50.9} bytes.
  • Authentication tag length: 256 bits 128 bits (see edit)
  • Authentication security: 128 bits 64 bits (cryptographic hash function)

EDIT: As mentioned above, this is actually not correct, because of an easy-to-miss detail in a previous section. There is no relevant encryption protocol change between 0.4.0 and 0.7.0.

Version 0.7.0 and Newer

And now we get to the latest version of the protocol, which is like the previous flavor, but now they truncate the HMAC-SHA-256 authentication tag to 128 bits in the actual steps to be performed.

Specifications should provide clear, unambiguous instructions for implementors. The OMEMO specification authors have failed to do this.

Interestingly, even the current version (0.8.3) doesn’t instruct implementations to use constant-time comparison for the truncated authentication tag in the decrypt path.

Surely, that won’t be a problem, right?

But to be congruent with the other sections in this historical breakdown, version 0.7.0+ looks like this:

  • Key entropy: 256 bits
  • Key commitment security: 64 bits
  • Safety limit: It’s complicated. It can range from 2^{47} to 2^{50.9} bytes.
  • Authentication tag length: 128 bits
  • Authentication security: 64 bits (cryptographic hash function)

Remarks

Because there is no rationale given for this sudden square-root reduction in security against existential forgery attacks (EDIT: not correct, it was always truncated, just poorly written), we kind of have to fill in the gaps and assume it was because of some kind of performance or bandwidth considerations.

But even that doesn’t really justify it, does it?

You’re only saving 16 bytes of bandwidth by truncating the MAC. Meanwhile, the actual ciphertext blobs are being encoded with base64, which adds 33% of overhead.

For any message larger than 48 bytes, this base64 encoding will dominate the bandwidth consumption more than using the full HMAC tag would.

Is truncating the HMAC tag to to 128 bits still secure? According to Signal, yes, it is. And I offer no disagreement to Signal’s assessment here.

The problem is, as I’ve said repeatedly, OMEMO’s specification makes no attempt to justify their design decisions.

The “why?” is left as an exercise to the reader, and I’m not convinced that they themselves fully know the answer to that question.

OMEMO Problem 3: Market Penetration

I alluded to this above, but it bears repeating: Even if the previous two problems were resolved overnight, it’s entirely possible to use XMPP without encryption.

Further, you can disable OMEMO even if you’re using a client that supports OMEMO.

When I compare it to Signal, whose users always have end-to-end encryption, XMPP + OMEMO falls short of what is required to be a secure private messaging app.

XMPP+OMEMO evangelists are quick to point out that Conversations, the favored implementation, lets you enforce “always use encryption” mode. This is a damn good idea, but there’s no universal guarantee that all clients will do this, nor make it the default behavior.

On that note, let’s talk about Conversations.

OMEMO Problem 4: You’re not ready for that Conversation

Conversations is, from the best I can gather, the most popular XMPP client with OMEMO support.

If you’re going to discuss OMEMO, in practical terms, you need to look at the most popular implementation of OMEMO to have an informed opinion on the matter. Only focusing on the specification, rathe than actual implementations, would be a foolhardy endeavor.

Conversations appears to follow the “everything but the kitchen sink” methodology to managing their complexity.

Just looking at the crypto folder, I count the following:

  1. Code that implements OpenPGP functions (signing, encryption)
  2. X.509 certificate validation for XMPP domains based on BouncyCastle
  3. An entire first-class SASL implementation, rather than a third-party library
  4. Finally, their OMEMO implementation (still named Axolotl) that depends on libsignal

Update (2024-08-09): An earlier version of this blog post criticized them for having two PGP classes in the crypto folder.

The second one (PgpDecryptionService.java) appeared to call a separate API from the one next to it, which seemed like a legacy code path that was in need of refactoring away.

A closer examination reveals that it calls a Service class that in turn calls the first PGP class (PgpEngine.java), so I retracted that item from the above list.

To be clear: These aren’t separate dependencies that Conversations pulls in to implement plugin supports. They’re first-party cryptographic implementations all within this Android app’s codebase.

(Some of them also have third-party dependencies to boot, but that’s generally okay.)

The only thing they didn’t include is their own first-party TLS implementation forked from, like, OpenSSL 1.0.2f. That would’ve filled my Bingo card.

Art by AJ

Your Version Check Bounced

The latest version of Conversations depends on version 1.64 of the Bouncy Castle S/MIME implementation (October 2019), despite 1.70 being the latest that supports Java 1.5 (and 1.78 being the latest overall, but requires Java 1.8).

This means the following security enhancements and fixes are absent in the version Conversations depends on:

  • Client-side OCSP Stapling was added in v1.65
  • TLS 1.3 support was added in v1.68
  • CVE-2023-33202, a DoS vulnerability in ASN.1 reachable through the PEM parser (i.e., any of the X.509 or OpenPGP code referenced above) is fixed in version v1.73.

The last one (CVE-2023-33202) is pernicious. Although it’s reachable through any usage of BouncyCastle’s Java PEM parser, the root cause is the underlying ASN.1 code, which means the domain verifier code is probably affected.

Why is Conversations in August 2024 still using an October 2019 version of their cryptography library?

Why am I pointing this out today when a component it provides that Conversations actually uses had a security fix in July 2023?

It’s not just BouncyCastle, either. Conversations also depends on a version of libsignal that was archived in 2022.

How We Got Here

Though it may be tempting for some readers to assign blame, let me be very clear: Doing so isn’t helpful, so don’t.

XMPP was a well-intentioned open protocol and Internet Standard. OMEMO was the least-bad effort to staple encryption onto XMPP.

If Conversations hadn’t cornered the XMPP market in recent years, the lack of discipline in complexity management (or a Dependabot or Semgrep integration, for that matter) would be a minor annoyance.

A lot of things had to go wrong for things to get as bad as they are.

Maybe part of the blame is a lack of investment or innovation in the XMPP developer community.

Maybe there should have been more dialogue between the security community and the XMPP Standards Foundation.

But the one thing I am certain of is the conclusion to this whole piece.

In Conclusion

As things stand today, I cannot recommend anyone use XMPP + OMEMO.

From the lack of a mechanism to keep implementations up-to-date with protocol versions, to a lack of clear rationale for protocol design decisions, to ecosystem issues with both the app availability and their third-party open source dependencies, to the most popular app (Conversations) being an absolute mess of complications, XMPP+OMEMO hasn’t earned my trust.

It’s possible that these flaws are correctable, and some future iteration of OMEMO will be better. It’s also possible that XMPP’s ecosystem will decide to make end-to-end encryption a non-optional component for all XMPP clients.

But I certainly wouldn’t bet my personal safety on good things happening; especially because of anything I wrote. I’m just some furry with a blog, after all.

“Don’t care, gonna keep using it!”

That’s fine. I’m not anyone’s boss or parent.

But in return, I really don’t appreciate unsolicited evangelism towards any technology.

It’s even worse when said evangelism is shouted over my informed opinions about cryptographic software. So, please don’t do that.

Addendum (2024-08-06)

Tim Henkes, one of the OMEMO authors, wrote a very tedious comment. The TL;DR of it is “you should distinguish between specification criticism and client software criticism, also truncation was in 0.4.0”.

I made the relevant changes above, because technical accuracy is important to me, but wasn’t interested in further involving myself with their project. So I replied saying as such.

Here’s a screenshot of their reply:

Responds with a meme accusing me of gleefully and deliberately spreading misinformation over the Internet.

I’m just gonna let that speak for itself.

If you’re curious about the cryptography used by other messaging apps, please refer to this page that collects my blogs about this topic.

By Soatok

Security engineer with a fursona. Ask me about dholes or Diffie-Hellman!

19 replies on “Against XMPP+OMEMO”

While I agree with the issues of missing rationale behind the specification changes, the lack of adoption of newer OMEMO versions and the lack of encrypt-by-default in most clients, I do think this blog post is missing true investigative depth. Some assumptions and critiques (e.g. if using the same key for multiple messages ever even happens, whether truncating the authentication tag was actually for performance or bandwidth improvements or why Conversations is sticking to older 3rd party libraries and if their vulnerabilities are ever actually exposed in the context of XMPP) are so easy to check, because all people concerned with this can be contacted very easily.

In fact, you probably just have to ask Daniel Gultsch about this and you’ll get comprehensive answers.

Instead, this blog post will just go around, everyone will have their own private little discussions about it and proper feedback will never land here. That’s just a shame.

The bigger shame here is that I needed to write about this at all. I barely have the time, let alone the energy, to have these conversations. But the cargo cultists were getting immensely annoying. and now they will either be motivated to contribute, or to shut the fuck up and stop bothering me. Either way, I win.

Ok, so this is just meant as a rant then. That’s fine, too. I was just hoping it could lead to some improvements.

I’m not interested in giving my labor for free to these projects. My only horse is this race was telling furries to not use Telegram. I recommended Signal. I never invited evangelists to annoy the shit out of me, or any of the other communications I’ve received over this. I do not have the time or emotional bandwidth for this shit.

if you’re speaking of the daniel gultsch who addressed meredith whittaker with, and i quote:

“Do I have to give another talk on how push notifications work? With @Mer__edith of @signalapp spreading lies and news outlets like @heiseonline amplifying their nonsense I feel like their is a lot of confusion out there on how push works. Google’s push notifications aren’t delivered by fairy dust and don’t consume any battery.

TLDR: You can de-google your phone. Install Conversations. It is the only app running in background. It will use #UnifiedPush to wake up other apps like Tusky and Ltt.rs.”

i’d rather eat my own hat than talk to the dude. there are better things in life than talking to a self-important reply human.

Daliel is pretty stubborn and rejects 95% of feature requests with super terse answers or no explanation at all, and I find that really annoying myself, yet somehow he single-handedly delivers actually usable stuff, often being the first to implement something, so I don’t complain much – there are forks that add what I find missing. I believe he deserves credit because XMPP as a messaging protocol for end users would definitely have been in a lot more sorry state now if his (non-perfect) client didn’t show up years ago and wasn’t being regularly updated since then. And AFAICS he can be quite helpful if you ask him specific technical questions about the protocol implementation.

Very interesting and well summarized thoughts, it will prove handy to have your post at the ready. Indeed quite often someone compares the two protocols and implies OMEMO is as mature as the current state of the art Signal protocol. Allow me to throw in the emerging post-quantum support that Signal is adding or already has in libsignal.

Hello cutie. To be clear, I’ve been reading your blog for over a year and following you on mastodon. Don’t get me wrong, but I think lately you’ve been taking people’s opinions too personally and trying to prove them wrong – remember, you can’t explain to morons that they’re wrong. You seem to be respected person and don’t have to prove anything to anyone. Just have fun.

This article highlights a number of reasons why we did not endorse/list XMPP on privacyguides.org (back then Privacy Tools).

Over the years we’ve had the various evangelists try to make an argument as to why we should (some of them quite persistent). It’s unfortunate but all other clients besides Conversations fell short and it seems that even Conversations itself may not be at the quality often advertised by the XMPP enthusiasts. The XMPP protocol was never really meant to be a privacy friendly design from what I can tell, and OMEMO was shoehorned in there after the fact. Originally the main users were in enterprise employees talking to other people within the company.

https://web.archive.org/web/20211215132539/https://infosec-handbook.eu/articles/xmpp-aitm/
https://notes.valdikss.org.ru/jabber.ru-mitm/

I also seem to remember, not sure where I read it, but I’m sure part of the design of Matrix was because of some of the issues with how XMPP was at the time. This is why the Matrix team put priority on making E2EE always on at least for private rooms and conversations. I’m sure we all remember when https://matrix.org/blog/2020/05/06/cross-signing-and-end-to-end-encryption-by-default-is-here/ happened.

I’m hoping we’ll see MLS at some point and have better performance in larger rooms https://arewemlsyet.com

> jabber.ru-mitm

AFAIK, this is not a protocol design or implementation problem, it’s someone – likely the police or another state actor – hijacked the physical server connection. It’s an attack on the hosting infra that allowed issuing rogue TLS certificates, and that, in turn, allowed the attacker to perform plain old TLS MiTM. This has nothing to do with XMPP itself as a protocol.

The post about AiTM post is valid.

> I’m hoping we’ll see MLS at some point and have better performance in larger rooms https://arewemlsyet.com

I just learned about https://nlnet.nl/project/XMPP-MLS/ today. It’s already in the works, or starting soon. In theory, this could allow skipping the complicated OMEMO upgrade path entirely and switch to an IETF standard instead at some point, but that’s in theory. XMPP ecosystem is, sadly, suffering from fragmentation, and time has proven that only a popular client like Conversation (+ its forks) can push the cart and create some momentum for the rest to follow. Projects like Signal don’t have that kind of problem due to being centralised / controlled by a single developer team / not having alternative implementations, i.e. not being fragmented.

I fully trust your misgivings on the actual cryptography here are real, and I would rather avoid them if I could. However, the *server software* being under my control is a hard requirement for me that Signal doesn’t meet, and I am never giving my phone number to a messaging service, nor require anyone else do so to talk to me. I don’t have any attachment to XMPP/OMEMO beyond this. Do you have anything else to recommend or keep an eye on that would address some of XMPP’s issues but not be centralized or phone-tied?

So, let’s take a step back. The entire saga here started with me telling furries to stop using Telegram. Telegram also requires a phone number to sign up (like Signal). Signal was a better recommendation than Telegram all around. And this context is important, because my audience is mostly furries that were previously using Telegram, and I really don’t want to spend more of my life reacting to XMPP, OMEMO, Matrix, etc. evangelism.

If you want the server software to be under your control, I don’t have a recommendation today. I’d suggest starting with something that uses MLS.

Any opinions about Wire? AFAIK, they were among the authors of MLS, but their messaging service is not there yet. Their current protocol, Proteus, was audited in 2017, but it’s been a long time since then. Even if the protocol didn’t change, the software sure did.

For cool_lantern – it’s E2EE-only, the server software is free and open source and self-hostable (but it’s a lot harder to do than, say, XMPP), the default public instance doesn’t require a phone number for registration, but, unlike Signal, the user directory is open and searchable (no numbers or emails are visible; AFAIK, only the usernames, nicknames, user IDs and photos can be viewed). I use it daily, but I cannot recommend it right now due to usability issues that have not been fixed for years. They’re slowly fixing them, but you’ll need a lot of patience. I guess their focus has been on corporate messaging for some years now, but I never tried the enterprise version. Oh, and the voice calls have the best audio quality among all VVoIP solutions I tried so far.

You could have a look at SimpleX Chat. At the very least it’s an interesting protocol. The clients are pretty usable, but lack a bit of polishing (but that could be said about XMPP clients, too) and you can run your own relay server.

Hi, thanks for your post. A few notes about Conversations forks here.

Quicksy doesn’t count because it’s exactly the same as Conversations and is made by the same author, even built from the same code base / repo. The only difference is it’s build around phone numbers, much like Telegram or Signal (before they introduced usernames and the ability to hide the number), or WhatsApp. The author says it’s not very popular: https://gultsch.social/@daniel/111923542300799098

Blabber.im is no longer updated ( https://codeberg.org/kriztan/blabber.im/commit/55842b6a0dfdf90f89940c340286d8173c013cc6 ).

Cheogram is another quite popular Conversations fork with additional features and optional PSTN gateway integration. It receives update quite frequently. https://git.sr.ht/~singpolyma/cheogram-android (available on F-Droid and Google Play).

There are other forks out there but I have never tried them.

I also have a question. If I understand correctly, you have found no problems with OMEMO as such, apart from:

1) not being on by default / not being enforced;
2) protocol / spec version incompatibility;
3) lack of clarity about spec changes.

Is that correct?

A dependency problem in Conversations is not a problem with OMEMO itself. It is true, though, that it’s the most widespread implementation, so that indeed makes OMEMO potentially less secure in practice, from a user point of view.

Other than that, your post is pretty on point. Keep going.

Hello there. Let’s say I want to do a crime. Signal starts by asking for a phone number to sign up. What good is better encryption if I can’t easily create an anonymous account? Is it still a better solution than XMPP and Matrix in this case?

First of all, if you want to commit a crime, leave me out of it.

Your comment is not a relevant reply to this particular blog post. It would have been better if it were a question on “What does it mean to be a Signal competitor?”

Anyone who cares about metadata resistance should look at Cwtch, Ricochet, or any other Tor-based solution. Not a mobile app. Not XMPP. Not Matrix.

I’ve only ever recommended Signal when recommending that Furries leave Telegram, which also requires a phone number to sign up. All secondary and tertiary concerns do not interest me, only how well they provide end-to-end encryption.

Comments are closed.