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.
Art: ScruffKerfluff
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.
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:
- libomemo, dependents:
- Conversations, forks:
- blabber.im
- Monocles
- Quicksy
- Snikket
- Converse.js
- Gajim
- jaxmpp, dependents:
- jabber-web
- MartinOMEMO, dependents:
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?
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.
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: messages, each with a max length of 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 bits64 bits - Safety limit: It’s complicated. It can range from to bytes.
- Authentication tag length:
256 bits128 bits (see edit) - Authentication security:
128 bits64 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 to 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:
- Code that implements OpenPGP functions (signing, encryption)
- X.509 certificate validation for XMPP domains based on BouncyCastle
- An entire first-class SASL implementation, rather than a third-party library
- 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 thecrypto
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:
I’m just gonna let that speak for itself.
https://soatok.blog/2024/08/04/against-xmppomemo/
#cryptography #endToEndEncryption #softwareSecurity
A lot of recent (and upcoming) blog posts I’ve written, and Fediverse discussions I’ve participated in, have been about the security of communication products.My criticism of these products is simply that, from a cryptography and security perspective, they’re not a real competitor to Signal.
For all its other faults, Signal sets the bar for secure private messaging. It’s a solid security tool, even if its user experience and feature set leaves a lot of people disappointed. I highly recommend it over, say, Telegram.
In response to my post about jettisoning Telegram, quite a few people have tried to evangelize other products. For example:
Edit: Oh yeah, DON’T USE SIGNAL. Use Matrix instead, offers the benefits of signal without the drawbacks of lack of sync and phone number requirements and is decentralized. The fact that everyone is going gaga for signal as “the BEST messaging app” should be a big red flag in and of itself, because hype trains like this aren’t organic, just saying.
So, let me explain what it means for a communication product to qualify as a Signal competitor from the perspective of someone whose job involves auditing cryptography implementations.The Minimum Bar to Clear
Open Source
Every private messaging app must be open source in order to qualify as a Signal competitor.If it’s not open source, it’s not even worth talking about.
End-to-End Encryption
Messages MUST be end-to-end encrypted. This means that you encrypt on one participant’s device, decrypt on another’s, and nobody in the middle can observe plaintext.When I say MUST, I mean the RFC 2119 keyword.
There must never be a “transmit plaintext” option. No excuses. Secure cryptography is not interoperable with insecure cryptography. If you allow a “transmit plaintext” mode for any reason whatsoever, you have failed to build an encryption product that meets the bar.
This disqualifies Matrix.
This disqualifies Telegram.
This disqualifies XMPP + OMEMO.
This alone disqualifies a lot of so-called private messaging apps.
This doesn’t mean your product is insecure, or that I’m aware of any specific ways to break it.
It just doesn’t occupy the same mindshare as Signal, which only transmits encrypted data and doesn’t have a plaintext protocol to downgrade to.
Therefore, it’s not a goddamn Signal alternative.
How You Encrypt Matters
Signal normalized the use of AES-256-CBC with HMAC-SHA256.Facebook’s “Secret Conversations” feature deviated from this and preferred AES-GCM for attachments, but this bit them when the Invisible Salamanders attack was discovered.
The way Signal uses AES+HMAC is fine for their use case, but building a secure committing AEAD mode (rather than merely AE) out of these primitives is nontrivial.
If you’re aiming to compete with Signal on security, you should, at minimum, expect to engage with a cryptography auditing firm at least once a year to review and re-review your protocol designs and implementations.
I Will Heavily Scrutinize Your Group Messaging Protocols
Group messaging is one of those topics that might sound easy if you can do peer-to-peer messaging securely, but is catastrophically difficult once you get into the details.See also: My blog post about Threema.
If you want a starting point, look at RFC 9420 (Messaging Layer Security, which is a group key agreement protocol for messaging apps).
How You Manage Keys Matters
Tox attempted to build atop NaCl’s crypto_box interface, but this is not suitable for a general purpose secure messaging due to a lack of KCI Security.Key management (which is the focus of an upcoming blog post) is a problem that almost everyone underestimates. It’s also the most user-facing aspect of these messaging applications.
WhatsApp uses Key Transparency to scale user trust. I’m proposing something similar for E2EE for the Fediverse.
This is a much better strategy than expecting users to manually verify “fingerprints”.
Don’t look at OpenPGP as a role model when it comes to user experience. Johnny still cannot fucking encrypt.
Your Feature Should Not Bypass Privacy
Want to add all sorts of frills, like video chat or some dumb bullshit with AI and/or blockchain to secure the attention of venture capitalist investors?You’d better not implement them in such a way that leaks users’ messages or search queries to your service.
The main reason Signal is “missing” features is because they are thoughtful about how these features are designed and implemented.
Guess what happens if you prioritize shipping features over privacy and cryptography engineering?
That’s right: You stop being a contender for a Signal alternative.
So What?
If your fave isn’t a viable alternative to Signal, don’t fucking recommend it to people in response to me recommending Signal.That’s all I ask.
Art: Scruff
But what about…?
I’m not here to discuss your use cases, or usability, or anything else. I’m also not saying that Signal is perfect!Signal is a private messaging app that I would feel safe recommending whistleblowers to use. It meets all these requirements.
In order to be a Signal competitor, no matter how much you like your app, it needs to meet them too, otherwise it isn’t a Signal competitor. Them’s the rules!
There may be other requirements that are more important to you, that Signal doesn’t meet. That’s fine! You can like other things.
But unless your favorite widget also meets all of the things on this page, it’s not a valid competitor from a security and privacy perspective, and therefore I don’t want to fucking hear about it in response to me saying “use Signal until something better comes along”.
Capiche?
Addendum (2024-08-01)
Since I originally posted this, there have been a lot of opinions expressed and questions asked about messaging apps that have nothing to do with cryptographic security.Those are good discussions to have, elsewhere. Hell, most of this discussion would be a better response to my other blog post than this one.
The goal of this post was to specify what the minimum bar is for a different app to qualify as a Signal competitor. It’s written from the perspective of someone whose career is in applied cryptography.
If you have thoughts, feelings, opinions, questions, or concerns about messaging apps (including but not limited to Signal), that’s wonderful.
But this specific blog post is not the correct place to voice them!
Especially if the first line of your response is “you’re too focused on [technology, security, cryptography] (select appropriate)”.
Because… no shit? That’s the entire point of this particular post. It’s narrowly scoped for a reason. Please respect that.
My upcoming vulnerability disclosure in Matrix will make the same point, but I wanted a separate, less distracting blog post to link people to when someone tries to evangelize another chat app instead of Signal, especially if they make security claims while doing so.
https://soatok.blog/2024/07/31/what-does-it-mean-to-be-a-signal-competitor/
#cryptography #endToEndEncryption #privateMessengers #Signal