Search
Items tagged with: endtoendencryption
In 2022, I wrote about my plan to build end-to-end encryption for the Fediverse. The goals were simple:
- Provide secure encryption of message content and media attachments between Fediverse users, as a new type of Direct Message which is encrypted between participants.
- Do not pretend to be a Signal competitor.
The primary concern at the time was “honest but curious” Fediverse instance admins who might snoop on another user’s private conversations.
After I finally was happy with the client-side secret key management piece, I had moved on to figure out how to exchange public keys. And that’s where things got complicated, and work stalled for 2 years.
Art: AJ
I wrote a series of blog posts on this complication, what I’m doing about it, and some other cool stuff in the draft specification.
- Towards Federated Key Transparency introduced the Public Key Directory project
- Federated Key Transparency Project Update talked about some of the trade-offs I made in this design
- Not supporting ECDSA at all, since FIPS 186-5 supports Ed25519
- Adding an account recovery feature, which power users can opt out of, that allows instance admins to help a user recover from losing all their keys
- Building a Key Transparency system that can tolerate GDPR Right To Be Forgotten takedown requests without invalidating history
- Introducing Alacrity to Federated Cryptography discussed how I plan to ensure that independent third-party clients stay up-to-date or lose the ability to decrypt messages
Recently, NIST published the new Federal Information Protection Standards documents for three post-quantum cryptography algorithms:
- FIPS-203 (ML-KEM, formerly known as CRYSTALS-Kyber),
- FIPS-204 (ML-DSA, formerly known as CRYSTALS-Dilithium)
- FIPS-205 (SLH-DSA, formerly known as SPHINCS+)
The race is now on to implement and begin migrating the Internet to use post-quantum KEMs. (Post-quantum signatures are less urgent.) If you’re curious why, this CloudFlare blog post explains the situation quite well.
Since I’m proposing a new protocol and implementation at the dawn of the era of post-quantum cryptography, I’ve decided to migrate the asymmetric primitives used in my proposals towards post-quantum algorithms where it makes sense to do so.
Art: AJ
The rest of this blog post is going to talk about technical specifics and the decisions I intend to make in both projects, as well as some other topics I’ve been thinking about related to this work.
Which Algorithms, Where?
I’ll discuss these choices in detail, but for the impatient:
- Public Key Directory
- Still just Ed25519 for now
- End-to-End Encryption
- KEMs: X-Wing (Hybrid X25519 and ML-KEM-768)
- Signatures: Still just Ed25519 for now
Virtually all other uses of cryptography is symmetric-key or keyless (i.e., hash functions), so this isn’t a significant change to the design I have in mind.
Post-Quantum Algorithm Selection Criteria
While I am personally skeptical if we will see a practical cryptography-relevant quantum computer in the next 30 years, due to various engineering challenges and a glacial pace of progress on solving them, post-quantum cryptography is still a damn good idea even if a quantum computer doesn’t emerge.
Post-Quantum Cryptography comes in two flavors:
- Key Encapsulation Mechanisms (KEMs), which I wrote about previously.
- Digital Signature Algorithms (DSAs).
Originally, my proposals were going to use Elliptic Curve Diffie-Hellman (ECDH) in order to establish a symmetric key over an untrusted channel. Unfortunately, ECDH falls apart in the wake of a crypto-relevant quantum computer. ECDH is the component that will be replaced by post-quantum KEMs.
Additionally, my proposals make heavy use of Edwards Curve Digital Signatures (EdDSA) over the edwards25519 elliptic curve group (thus, Ed25519). This could be replaced with a post-quantum DSA (e.g., ML-DSA) and function just the same, albeit with bandwidth and/or performance trade-offs.
But isn’t post-quantum cryptography somewhat new?
Lattice-based cryptography has been around almost as long as elliptic curve cryptography. One of the first designs, NTRU, was developed in 1996.
Meanwhile, ECDSA was published in 1992 by Dr. Scott Vanstone (although it was not made a standard until 1999). Lattice cryptography is pretty well-understood by experts.
However, before the post-quantum cryptography project, there hasn’t been a lot of incentive for attackers to study lattices (unless they wanted to muck with homomorphic encryption).
So, naturally, there is some risk of a cryptanalysis renaissance after the first post-quantum cryptography algorithms are widely deployed to the Internet.
However, this risk is mostly a concern for KEMs, due to the output of a KEM being the key used to encrypt sensitive data. Thus, when selecting KEMs for post-quantum security, I will choose a Hybrid construction.
Hybrid what?
We’re not talking folfs, sonny!
Hybrid isn’t just a thing that furries do with their fursonas. It’s also a term that comes up a lot in cryptography.
Unfortunately, it comes up a little too much.
I made this dumb meme with imgflip
When I say we use Hybrid constructions, what I really mean is we use a post-quantum KEM and a classical KEM (such as HPKE‘s DHKEM), then combine them securely using a KDF.
Post-quantum KEMs
For the post-quantum KEM, we only really have one choice: ML-KEM. But this choice is actually three choices: ML-KEM-512, ML-KEM-768, or ML-KEM-1024.
The security margin on ML-KEM-512 is a little tight, so most cryptographers I’ve talked with recommend ML-KEM-768 instead.
Meanwhile, the NSA wants the US government to use ML-KEM-1024 for everything.
How will you hybridize your post-quantum KEM?
Originally, I was looking to use DHKEM with X25519, as part of the HPKE specification. After switching to post-quantum cryptography, I would need to combine it with ML-KEM-768 in such a way that the whole shebang is secure if either component is secure.
But then, why reinvent the wheel here? X-Wing already does that, and has some nice binding properties that a naive combination might not.
So let’s use X-Wing for our KEM.
Notably, OpenMLS is already doing this in their next release.
Art: CMYKat
Post-quantum signatures
So our KEM choice seems pretty straightforward. What about post-quantum signatures?
Do we even need post-quantum signatures?
Well, the situation here is not nearly as straightforward as KEMs.
For starters, NIST chose to standardize two post-quantum digital signature algorithms (with a third coming later this year). They are as follows:
- ML-DSA (formerly CRYSTALS-Dilithium), that comes in three flavors:
- ML-DSA-44
- ML-DSA-65
- ML-DSA-87
- SLH-DSA (formerly SPHINCS+), that comes in 24 flavors
- FN-DSA (formerly FALCON), that comes in two flavors but may be excruciating to implement in constant-time (this one isn’t standardized yet)
Since we’re working at the application layer, we’re less worried about a few kilobytes of bandwidth than the networking or X.509 folks are. Relatively speaking, we care about security first, performance second, and message size last.
After all, people ship Electron, React Native, and NextJS apps that load megabytes of JavaScript code to print, “hello world,” and no one bats an eye. A few kilobytes in this context is easily digestible for us.
(As I said, this isn’t true for all layers of the stack. WebPKI in particular feels a lot of pain with large public keys and/or signatures.)
Eliminating post-quantum signature candidates
Performance considerations would eliminate SLH-DSA, which is the most conservative choice. Even with the fastest parameter set (SLH-DSA-128f), this family of algorithms is about 550x slower than Ed25519. (If we prioritize bandwidth, it becomes 8000x slower.)
Adopted from CloudFlare’s blog post on post-quantum cryptography.
Between the other two, FN-DSA is a tempting option. Although it’s difficult to implement in constant-time, it offers smaller public key and signature sizes.
However, FN-DSA is not standardized yet, and it’s only known to be safe on specific hardware architectures. (It might be safe on others, but that’s not proven yet.)
In order to allow Fediverse users be secure on a wider range of hardware, this uncertainty would limit our choice of post-quantum signature algorithms to some flavor of ML-DSA–whether stand-alone or in a hybrid construction.
Unlike KEMs, hybrid signature constructions may be problematic in subtle ways that I don’t want to deal with. So if we were to do anything, we would probably choose a pure post-quantum signature algorithm.
Against the Early Adoption of Post-Quantum Signatures
There isn’t an immediate benefit to adopting a post-quantum signature algorithm, as David Adrian explains.
The migration to post-quantum cryptography will be a long and difficult road, which is all the more reason to make sure we learn from past efforts, and take advantage of the fact the risk is not imminent. Specifically, we should avoid:
- Standardizing without real-world experimentation
- Standardizing solutions that match how things work currently, but have significant negative externalities (increased bandwidth usage and latency), instead of designing new things to mitigate the externalities
- Deploying algorithms pre-standardization in ways that can’t be easily rolled back
- Adding algorithms that are pre-standardization or have severe shortcomings to compliance frameworks
We are not in the middle of a post-quantum emergency, and nothing points to a surprise “Q-Day” within the next decade. We have time to do this right, and we have time for an iterative feedback loop between implementors, cryptographers, standards bodies, and policymakers.
The situation may change. It may become clear that quantum computers are coming in the next few years. If that happens, the risk calculus changes and we can try to shove post-quantum cryptography into our existing protocols as quickly as possible. Thankfully, that’s not where we are.
David Adrian, Lack of post-quantum security is not plaintext.
Furthermore, there isn’t currently any commitment from the Sigsum developers to adopt a post-quantum signature scheme in the immediate future. They hard-code Ed25519 for the current iteration of the specification.
The verdict on digital signature algorithms?
Given all of the above, I’m going to opt to simply not adopt post-quantum signatures until a later date.
Version 1 of our design will continue to use Ed25519 despite it not being secure after quantum computers emerge (“Q-Day”).
When the security industry begins to see warning signs of Q-Day being realistically within a decade, we will prioritize migrating to use post-quantum signature algorithms in a new version of our design.
Should something drastic happen that would force us to decide on a post-quantum algorithm today, we would choose ML-DSA-44. However, that’s unlikely for at least several years.
Remember, Store Now, Decrypt Later doesn’t really break signatures the way it would break public-key encryption.
Art: Harubaki
Miscellaneous Technical Matters
Okay, that’s enough about post-quantum for now. I worry that if I keep talking about key encapsulation, some of my regular readers will start a shitty garage band called My KEMical Romance before the end of the year.
Let’s talk about some other technical topics related to end-to-end encryption for the Fediverse!
Federated MLS
MLS was implicitly designed with the idea of having one central service for passing messages around. This makes sense if you’re building a product like Signal, WhatsApp, or Facebook Messenger.
It’s not so great for federated environments where your Delivery Service may be, in fact, more than one service (i.e., the Fediverse). An expired Internet Draft for Federated MLS talks about these challenges.
If we wanted to build atop MLS for group key agreement (like has been suggested before), we’d need to tackle this in a way that doesn’t cede control of MLS epochs to any server that gets compromised.
How to Make MLS Tolerate Federation
First, the Authentication Service component can be replaced by client-side protocols, where public keys are sourced from the Public Key Directory (PKD) services.
That is to say, from the PKD, you can fetch a valid list of Ed25519 public keys for each participant in the group.
When a group is created, the creator’s Ed25519 public key is known. Everyone they invite, their software necessarily has to know their Ed25519 public key in order to invite them.
In order for a group action to be performed, it must be signed by one of the public keys enrolled into the group list. Additionally, some actions may be limited by permissions attached at the time of the invite (or elevated by a more privileged user; which necessitates another group action).
By requiring a valid signature from an existing group member, we remove the capability of the Fediverse instance that’s hosting the discussion group to meddle with it in any way (unless, for some reason, the server is somehow also a participant that was invited).
But therein lies the other change we need to make: In many cases, groups will span multiple Fediverse servers, so groups shouldn’t be dependent on a single instance.
Spreading The Load Across Instances
Put simply, we need a consensus algorithm to determine which instance hosts messages. We could look to Raft as a starting point, but whatever we land on should be fair, fault-tolerant, and deterministic to all participants who can agree on the same symmetric keying material at some point in time.
To that end, I propose using an additional HKDF output from the Group Key Agreement protocol to select a “leader” for all instances involved in the group, weighted by the number of participants on each instance.
Then, every N messages (where N >= 1), a new leader is elected by the same deterministic protocol. This will be performed entirely client-side, and clients will choose N. I will refer to this as a sub-epoch, since it doesn’t coincide with a new MLS epoch.
Since the agreed-upon group key always ratchets forward when a group action occurs (i.e., whenever there’s a new epoch), getting another KDF output to elect the next leader is straightforward.
This isn’t a fully fleshed out idea. Building consensus protocols that can handle real-world operational issues is heavily specialized work and there’s a high risk of falling to the illusion of safety until it’s too late. I will probably need help with this component.
That said, we aren’t building an anonymity network, so the cost of getting a detail wrong isn’t measurable in blood.
We aren’t really concerned with Sybil attacks. Winning the election just means you’re responsible for being a dumb pipe for ciphertext. Client software should trust the instance software as little as possible.
We also probably don’t need to worry about availability too much. Since we’re building atop ActivityPub, when a server goes down, the other instances can hold encrypted messages in the outbox for the host instance to pick up when it’s back online.
If that’s not satisfactory, we could also select both a primary and secondary leader for each epoch (and sub-epoch), to have built-in fail-over when more than one instance is involved in a group conversation.
If messages aren’t being delivered for an unacceptable period of time, client software can forcefully initiate a new leader election by expiring the current MLS epoch (i.e. by rotating their own public key and sending the relevant bundle to all other participants).
Art: Kyume
Those are just some thoughts. I plan to talk it over with people who have more expertise in the relevant systems.
And, as with the rest of this project, I will write a formal specification for this feature before I write a single line of production code.
Abuse Reporting
I could’ve swore I talked about this already, but I can’t find it in any of my previous ramblings, so here’s a good place as any.
The intent for end-to-end encryption is privacy, not secrecy.
What does this mean exactly? From the opening of Eric Hughes’ A Cypherpunk’s Manifesto:
Privacy is necessary for an open society in the electronic age. Privacy is not secrecy.A private matter is something one doesn’t want the whole world to know, but a secret matter is something one doesn’t want anybody to know.
Privacy is the power to selectively reveal oneself to the world.
Eric Hughes (with whitespace and emphasis added)
Unrelated: This is one reason why I use “secret key” when discussing asymmetric cryptography, rather than “private key”. It also lends towards sk
and pk
as abbreviations, whereas “private” and “public” both start with the letter P, which is annoying.
With this distinction in mind, abuse reporting is not inherently incompatible with end-to-end encryption or any other privacy technology.
In fact, it’s impossible to create useful social technology without the ability for people to mitigate abuse.
So, content warning: This is going to necessarily discuss some gross topics, albeit not in any significant detail. If you’d rather not read about them at all, feel free to skip this section.
Art: CMYKat
When thinking about the sorts of problems that call for an abuse reporting mechanism, you really need to consider the most extreme cases, such as someone joining group chats to spam unsuspecting users with unsolicited child sexual abuse material (CSAM), flashing imagery designed to trigger seizures, or graphic depictions of violence.
That’s gross and unfortunate, but the reality of the Internet.
However, end-to-end encryption also needs to prioritize privacy over appeasing lazy cops who would rather everyone’s devices include a mandatory little cop that watches all your conversations and snitches on you if you do anything that might be illegal, or against the interest of your government and/or corporate masters. You know the type of cop. They find privacy and encryption to be rather inconvenient. After all, why bother doing their jobs (i.e., actual detective work) when you can just criminalize end-to-end encryption and use dragnet surveillance instead?
Whatever we do, we will need to strike a balance that protects users’ privacy, without any backdoors or privileged access for lazy cops, with community safety.
Thus, the following mechanisms must be in place:
- Groups must have the concept of an “admin” role, who can delete messages on behalf of all users and remove users from the group. (Signal currently doesn’t have this.)
- Users must be able to delete messages on their own device and block users that send abusive content. (The Fediverse already has this sort of mechanism, so we don’t need to be inventive here.)
- Users should have the ability to report individual messages to the instance moderators.
I’m going to focus on item 3, because that’s where the technically and legally thorny issues arise.
Keep in mind, this is just a core-dump of thoughts about this topic, and I’m not committing to anything right now.
Technical Issues With Abuse Reporting
First, the end-to-end encryption must be immune to Invisible Salamanders attacks. If it’s not, go back to the drawing board.
Every instance will need to have a moderator account, who can receive abuse reports from users. This can be a shared account for moderators or a list of moderators maintained by the server.
When an abuse report is sent to the moderation team, what needs to happen is that the encryption keys for those specific messages are re-wrapped and sent to the moderators.
So long as you’re using a forward-secure ratcheting protocol, this doesn’t imply access to the encryption keys for other messages, so the information disclosed is limited to the messages that a participant in the group consents to disclosing. This preserves privacy for the rest of the group chat.
When receiving a message, moderators should not only be able to see the reported message’s contents (in the order that they were sent), but also how many messages were omitted in the transcript, to prevent a type of attack I colloquially refer to as “trolling through omission”. This old meme illustrates the concept nicely:
Trolling through omission.
And this all seems pretty straightforward, right? Let users protect themselves and report abuse in such a way that doesn’t invalidate the privacy of unrelated messages or give unfettered access to the group chats. “Did Captain Obvious write this section?”
But things aren’t so clean when you consider the legal ramifications.
Potential Legal Issues With Abuse Reporting
Suppose Alice, Bob, and Troy start an encrypted group conversation. Alice is the group admin and delete messages or boot people from the chat.
One day, Troy decides to send illegal imagery (e.g., CSAM) to the group chat.
Bob immediately, disgusted, reports it to his instance moderator (Dave) as well as Troy’s instance moderator (Evelyn). Alice then deletes the messages for her and Bob and kicks Troy from the chat.
Here’s where the legal questions come in.
If Dave and Evelyn are able to confirm that Troy did send CSAM to Alice and Bob, did Bob’s act of reporting the material to them count as an act of distribution (i.e., to Dave and/or Evelyn, who would not be able to decrypt the media otherwise)?
If they aren’t able to confirm the reports, does Alice’s erasure count as destruction of evidence (i.e., because they cannot be forwarded to law enforcement)?
Are Bob and Alice legally culpable for possession? What about Dave and Evelyn, whose servers are hosting the (albeit encrypted) material?
It’s not abundantly clear how the law will intersect with technology here, nor what specific technical mechanisms would need to be in place to protect Alice, Bob, Dave, and Evelyn from a particularly malicious user like Troy.
Obviously, I am not a lawyer. I have an understanding with my lawyer friends that I will not try to interpret law or write my own contracts if they don’t roll their own crypto.
That said, I do have some vague ideas for mitigating the risk.
Ideas For Risk Mitigation
To contend with this issue, one thing we could do is separate the abuse reporting feature from the “fetch and decrypt the attached media” feature, so that while instance moderators will be capable of fetching the reported abuse material, it doesn’t happen automatically.
When the “reason” attached to an abuse report signals CSAM in any capacity, the client software used by moderators could also wholesale block the download of said media.
Whether that would be sufficient mitigate the legal matters raised previously, I can’t say.
And there’s still a lot of other legal uncertainty to figure out here.
- Do instance moderators actually have a duty to forward CSAM reports to law enforcement?
- If so, how should abuse forwarding to be implemented?
- How do we train law enforcement personnel to receive and investigate these reports WITHOUT frivolously arresting the wrong people or seizing innocent Fediverse servers?
- How do we ensure instance admins are broadly trained to handle this?
- How do we deal with international law?
- How do we prevent scope creep?
- While there is public interest in minimizing the spread of CSAM, which is basically legally radioactive, I’m not interested in ever building a “snitch on women seeking reproductive health care in a state where abortion is illegal” capability.
- Does Section 230 matter for any of these questions?
We may not know the answers to these questions until the courts make specific decisions that establish relevant case law, or our governments pass legislation that clarifies everyone’s rights and responsibilities for such cases.
Until then, the best answer may simply to do nothing.
That is to say, let admins delete messages for the whole group, let users delete messages they don’t want on their own hardware, and let admins receive abuse reports from their users… but don’t do anything further.
Okay, we should definitely require an explicit separate action to download and decrypt the media attached to a reported message, rather than have it be automatic, but that’s it.
What’s Next?
For the immediate future, I plan on continuing to develop the Federated Public Key Directory component until I’m happy with its design. Then, I will begin developing the reference implementations for both client and server software.
Once that’s in a good state, I will move onto finishing the E2EE specification. Then, I will begin building the client software and relevant server patches for Mastodon, and spinning up a testing instance for folks to play with.
Timeline-wise, I would expect most of this to happen in 2025.
I wish I could promise something sooner, but I’m not fond of moving fast and breaking things, and I do have a full time job unrelated to this project.
Hopefully, by the next time I pen an update for this project, we’ll be closer to launching. (And maybe I’ll have answers to some of the legal concerns surrounding abuse reporting, if we’re lucky.)
https://soatok.blog/2024/09/13/e2ee-for-the-fediverse-update-were-going-post-quantum/
#E2EE #endToEndEncryption #fediverse #FIPS #Mastodon #postQuantumCryptography
Update (2024-06-06): There is an update on this project.As Twitter’s new management continues to nosedive the platform directly into the ground, many people are migrating to what seem like drop-in alternatives; i.e. Cohost and Mastodon. Some are even considering new platforms that none of us have heard of before (one is called “Hive”).
Needless to say, these are somewhat chaotic times.
One topic that has come up several times in the past few days, to the astonishment of many new Mastodon users, is that Direct Messages between users aren’t end-to-end encrypted.
And while that fact makes Mastodon DMs no less safe than Twitter DMs have been this whole time, there is clearly a lot of value and demand in deploying end-to-end encryption for ActivityPub (the protocol that Mastodon and other Fediverse software uses to communicate).
However, given that Melon Husk apparently wants to hurriedly ship end-to-end encryption (E2EE) in Twitter, in some vain attempt to compete with Signal, I took it upon myself to kickstart the E2EE effort for the Fediverse.
https://twitter.com/elonmusk/status/1519469891455234048
So I’d like to share my thoughts about E2EE, how to design such a system from the ground up, and why the direction Twitter is heading looks to be security theater rather than serious cryptographic engineering.
If you’re not interested in those things, but are interested in what I’m proposing for the Fediverse, head on over to the GitHub repository hosting my work-in-progress proposal draft as I continue to develop it.
How to Quickly Build E2EE
If one were feeling particularly cavalier about your E2EE designs, they could just generate then dump public keys through a server they control, pass between users, and have them encrypt client-side. Over and done. Check that box.Every public key would be ephemeral and implicitly trusted, and the threat model would mostly be, “I don’t want to deal with law enforcement data requests.”
Hell, I’ve previously written an incremental blog post to teach developers about E2EE that begins with this sort of design. Encrypt first, ratchet second, manage trust relationships on public keys last.
If you’re catering to a slightly tech-savvy audience, you might throw in SHA256(pk1 + pk2) -> hex2dec() and call it a fingerprint / safety number / “conversation key” and not think further about this problem.
Look, technical users can verify out-of-band that they’re not being machine-in-the-middle attacked by our service.An absolute fool who thinks most people will ever do this
From what I’ve gathered, this appears to be the direction that Twitter is going.https://twitter.com/wongmjane/status/1592831263182028800
Now, if you’re building E2EE into a small hobby app that you developed for fun (say: a World of Warcraft addon for erotic roleplay chat), this is probably good enough.
If you’re building a private messaging feature that is intended to “superset Signal” for hundreds of millions of people, this is woefully inadequate.
https://twitter.com/elonmusk/status/1590426255018848256
Art: LvJ
If this is, indeed, the direction Musk is pushing what’s left of Twitter’s engineering staff, here is a brief list of problems with what they’re doing.
- Twitter Web. How do you access your E2EE DMs after opening Twitter in your web browser on a desktop computer?
- If you can, how do you know twitter.com isn’t including malicious JavaScript to snarf up your secret keys on behalf of law enforcement or a nation state with a poor human rights record?
- If you can, how are secret keys managed across devices?
- If you use a password to derive a secret key, how do you prevent weak, guessable, or reused passwords from weakening the security of the users’ keys?
- If you cannot, how do users decide which is their primary device? What if that device gets lost, stolen, or damaged?
- Authenticity. How do you reason about the person you’re talking with?
- Forward Secrecy. If your secret key is compromised today, can you recover from this situation? How will your conversation participants reason about your new Conversation Key?
- Multi-Party E2EE. If a user wants to have a three-way E2EE DM with the other members of their long-distance polycule, does Twitter enable that?
- How are media files encrypted in a group setting? If you fuck this up, you end up like Threema.
- Is your group key agreement protocol vulnerable to insider attacks?
- Cryptography Implementations.
- What does the KEM look like? If you’re using ECC, which curve? Is a common library being used in all devices?
- How are you deriving keys? Are you just using the result of an elliptic curve (scalar x point) multiplication directly without hashing first?
- Independent Third-Party Review.
- Who is reviewing your protocol designs?
- Who is reviewing your cryptographic primitives?
- Who is reviewing the code that interacts with E2EE?
- Is there even a penetration test before the feature launches?
As more details about Twitter’s approach to E2EE DMs come out, I’m sure the above list will be expanded with even more questions and concerns.
My hunch is that they’ll reuse liblithium (which uses Curve25519 and Gimli) for Twitter DMs, since the only expert I’m aware of in Musk’s employ is the engineer that developed that library for Tesla Motors. Whether they’ll port it to JavaScript or just compile to WebAssembly is hard to say.
How To Safely Build E2EE
You first need to decompose the E2EE problem into five separate but interconnected problems.
- Client-Side Secret Key Management.
- Multi-device support
- Protect the secret key from being pilfered (i.e. by in-browser JavaScript delivered from the server)
- Public Key Infrastructure and Trust Models.
- TOFU (the SSH model)
- X.509 Certificate Authorities
- Certificate/Key/etc. Transparency
- SigStore
- PGP’s Web Of Trust
- Key Agreement.
- While this is important for 1:1 conversations, it gets combinatorially complex when you start supporting group conversations.
- On-the-Wire Encryption.
- Direct Messages
- Media Attachments
- Abuse-resistance (i.e. message franking for abuse reporting)
- The Construction of the Previous Four.
- The vulnerability of most cryptographic protocols exists in the joinery between the pieces, not the pieces themselves. For example, Matrix.
This might not be obvious to someone who isn’t a cryptography engineer, but each of those five problems is still really hard.
To wit: The latest IETF RFC draft for Message Layer Security, which tackles the Key Agreement problem above, clocks in at 137 pages.
Additionally, the order I specified these problems matters; it represents my opinion of which problem is relatively harder than the others.
When Twitter’s CISO, Lea Kissner, resigned, they lost a cryptography expert who was keenly aware of the relative difficulty of the first problem.
https://twitter.com/LeaKissner/status/1592937764684980224
You may also notice the order largely mirrors my previous guide on the subject, in reverse. This is because teaching a subject, you start with the simplest and most familiar component. When you’re solving problems, you generally want the opposite: Solve the hardest problems first, then work towards the easier ones.
This is precisely what I’m doing with my E2EE proposal for the Fediverse.
The Journey of a Thousand Miles Begins With A First Step
Before you write any code, you need specifications.Before you write any specifications, you need a threat model.
Before you write any threat models, you need both a clear mental model of the system you’re working with and how the pieces interact, and a list of security goals you want to achieve.
Less obviously, you need a specific list of non-goals for your design: Properties that you will not prioritize. A lot of security engineering involves trade-offs. For example: elliptic curve choice for digital signatures is largely a trade-off between speed, theoretical security, and real-world implementation security.
If you do not clearly specify your non-goals, they still exist implicitly. However, you may find yourself contradicting them as you change your mind over the course of development.
Being wishy-washy about your security goals is a good way to compromise the security of your overall design.
In my Mastodon E2EE proposal document, I have a section called Design Tenets, which states the priorities used to make trade-off decisions. I chose Usability as the highest priority, because of AviD’s Rule of Usability.
Security at the expense of usability comes at the expense of security.Avi Douglen, Security StackExchange
Underneath Tenets, I wrote Anti-Tenets. These are things I explicitly and emphatically do not want to prioritize. Interoperability with any incumbent designs (OpenPGP, Matrix, etc.) is the most important anti-tenet when it comes to making decisions. If our end-state happens to interop with someone else’s design, cool. I’m not striving for it though!Finally, this section concludes with a more formal list of Security Goals for the whole project.
Art: LvJ
Every component (from the above list of five) in my design will have an additional dedicated Security Goals section and Threat Model. For example: Client-Side Secret Key Management.
You will then need to tackle each component independently. The threat model for secret-key management is probably the trickiest. The actual encryption of plaintext messages and media attachments is comparatively simple.
Finally, once all of the pieces are laid out, you have the monumental (dare I say, mammoth) task of stitching them together into a coherent, meaningful design.
If you did your job well at the outset, and correctly understand the architecture of the distributed system you’re working with, this will mostly be straightforward.
Making Progress
At every step of the way, you do need to stop and ask yourself, “If I was an absolute chaos gremlin, how could I fuck with this piece of my design?” The more pieces your design has, the longer the list of ways to attack it will grow.It’s also helpful to occasionally consider formal methods and security proofs. This can have surprising implications for how you use some algorithms.
You should also be familiar enough with the cryptographic primitives you’re working with before you begin such a journey; because even once you’ve solved the key management story (problems 1, 2 and 3 from the above list of 5), cryptographic expertise is still necessary.
- If you’re feeding data into a hash function, you should also be thinking about domain separation. More information.
- If you’re feeding data into a MAC or signature algorithm, you should also be thinking about canonicalization attacks. More information.
- If you’re encrypting data, you should be thinking about multi-key attacks and confused deputy attacks. Also, the cryptographic doom principle if you’re not using IND-CCA3 algorithms.
- At a higher-level, you should proactively defend against algorithm confusion attacks.
How Do You Measure Success?
It’s tempting to call the project “done” once you’ve completed your specifications and built a prototype, and maybe even published a formal proof of your design, but you should first collect data on every important metric:
- How easy is it to use your solution?
- How hard is it to misuse your solution?
- How easy is it to attack your solution? Which attackers have the highest advantage?
- How stable is your solution?
- How performant is your solution? Are the slow pieces the deliberate result of a trade-off? How do you know the balance was struck corectly?
Where We Stand Today
I’ve only begun writing my proposal, and I don’t expect it to be truly ready for cryptographers or security experts to review until early 2023.However, my clearly specified tenets and anti-tenets were already useful in discussing my proposal on the Fediverse.
@soatok @fasterthanlime Should probably embed the algo used for encryption in the data used for storing the encrypted blob, to support multiples and future changes.@fabienpenso@hachyderm.io proposes in-band protocol negotiation instead of versioned protocols
The main things I wanted to share today are:
- The direction Twitter appears to be heading with their E2EE work, and why I think it’s a flawed approach
- Designing E2EE requires a great deal of time, care, and expertise; getting to market quicker at the expense of a clear and careful design is almost never the right call
Mastodon? ActivityPub? Fediverse? OMGWTFBBQ!
In case anyone is confused about Mastodon vs ActivityPub vs Fediverse lingo:The end goal of my proposal is that I want to be able to send DMs to queer furries that use Mastodon such that only my recipient can read them.
Achieving this end goal almost exclusively requires building for ActivityPub broadly, not Mastodon specifically.
However, I only want to be responsible for delivering this design into the software I use, not for every single possible platform that uses ActivityPub, nor all the programming languages they’re written in.
I am going to be aggressive about preventing scope creep, since I’m doing all this work for free. (I do have a Ko-Fi, but I won’t link to it from here. Send your donations to the people managing the Mastodon instance that hosts your account instead.)
My hope is that the design documents and technical specifications become clear enough that anyone can securely implement end-to-end encryption for the Fediverse–even if special attention needs to be given to the language-specific cryptographic libraries that you end up using.
Art: LvJ
Why Should We Trust You to Design E2EE?
This sort of question comes up inevitably, so I’d like to tackle it preemptively.My answer to every question that begins with, “Why should I trust you” is the same: You shouldn’t.
There are certainly cryptography and cybersecurity experts that you will trust more than me. Ask them for their expert opinions of what I’m designing instead of blanketly trusting someone you don’t know.
I’m not interested in revealing my legal name, or my background with cryptography and computer security. Credentials shouldn’t matter here.
If my design is good, you should be able to trust it because it’s good, not because of who wrote it.
If my design is bad, then you should trust whoever proposes a better design instead. Part of why I’m developing it in the open is so that it may be forked by smarter engineers.
Knowing who I am, or what I’ve worked on before, shouldn’t enter your trust calculus at all. I’m a gay furry that works in the technology industry and this is what I’m proposing. Take it or leave it.
Why Not Simply Rubber-Stamp Matrix Instead?
(This section was added on 2022-11-29.)There’s a temptation, most often found in the sort of person that comments on the /r/privacy subreddit, to ask why even do all of this work in the first place when Matrix already exists?
The answer is simple: I do not trust Megolm, the protocol designed for Matrix.
Megolm has benefited from amateur review for four years. Non-cryptographers will confuse this observation with the proposition that Matrix has benefited from peer review for four years. Those are two different propositions.
In fact, the first time someone with cryptography expertise bothered to look at Matrix for more than a glance, they found critical vulnerabilities in its design. These are the kinds of vulnerabilities that are not easily mitigated, and should be kept in mind when designing a new protocol.
You don’t have to take my word for it. Listen to the Security, Cryptography, Whatever podcast episode if you want cryptographic security experts’ takes on Matrix and these attacks.
From one of the authors of the attack paper:
So they kind of, after we disclosed to them, they shared with us their timeline. It’s not fixed yet. It’s a, it’s a bigger change because they need to change the protocol. But they always said like, Okay, fair enough, they’re gonna change it. And they also kind of announced a few days after kind of the public disclosure based on the public reaction that they should prioritize fixing that. So it seems kind of in the near future, I don’t have the timeline in front of me right now. They’re going to fix that in the sense of like the— because there’s, notions of admins and so on. So like, um, so authenticating such group membership requests is not something that is kind of completely outside of, kind of like the spec. They just kind of need to implement the appropriate authentication and cryptography.Martin Albrecht, SCW podcast
From one of the podcast hosts:I guess we can at the very least tell anyone who’s going forward going to try that, that like, yes indeed. You should have formal models and you should have proofs. And so there’s this, one of the reactions to kind of the kind of attacks that we presented and also to prior previous work where we kind of like broken some cryptographic protocols is then to say like, “Well crypto’s hard”, and “don’t roll your own crypto.” But in a way the thing is like, you know, we need some people to roll their own crypto because that’s how we have crypto. Someone needs to roll it. But we have developed techniques, we have developed formalisms, we have developed methods for making sure it doesn’t have to be hard, it’s not, it’s not a dark art kind of that only kind of a few, a select few can master, but it’s, you know, it’s a science and you can learn it. So, but you need to then indeed employ a cryptographer in kind of like forming, modeling your protocol and whenever you make changes, then, you know, they need to look over this and say like, Yes, my proof still goes through. Um, so like that is how you do this. And then, then true engineering is still hard and it will remain hard and you know, any science is hard, but then at least you have some confidence in what you’re doing. You might still then kind of on the space and say like, you know, the attack surface is too large and I’m not gonna to have an encrypted backup. Right. That’s then the problem of a different hard science, social science. Right. But then just use the techniques that we have, the methods that we have to establish what we need.Thomas Ptacek, SCW podcast
It’s tempting to listen to these experts and say, “OK, you should use libsignal instead.”But libsignal isn’t designed for federation and didn’t prioritize group messaging. The UX for Signal is like an IM application between two parties. It’s a replacement for SMS.
It’s tempting to say, “Okay, but you should use MLS then; never roll your own,” but MLS doesn’t answer the group membership issue that plagued Matrix. It punts on these implementation details.
Even if I use an incumbent protocol that privacy nerds think is good, I’ll still have to stitch it together in a novel manner. There is no getting around this.
Maybe wait until I’ve finished writing the specifications for my proposal before telling me I shouldn’t propose anything.
Credit for art used in header: LvJ, Harubaki
https://soatok.blog/2022/11/22/towards-end-to-end-encryption-for-direct-messages-in-the-fediverse/
There are two mental models for designing a cryptosystem that offers end-to-end encryption to all of its users.
The first is the Signal model.
Predicated on Moxie’s notion that the ecosystem is moving, Signal (and similar apps) maintain some modicum of centralized control over the infrastructure and deployment of their app. While there are obvious downsides to this approach, it allows them to quickly roll out ecosystem-wide changes to their encryption protocols without having to deal with third-party clients falling behind.
The other is the federated model, which is embraced by Matrix, XMPP with OMEMO, and other encrypted chat apps and protocols.
This model can be attractive to a lot of people whose primary concern is data sovereignty rather than cryptographic protections. (Most security experts care about both aspects, but we differ in how they rank the two priorities relative to each other.)
As I examined in my criticisms of Matrix and XMPP+OMEMO, they kind of prove Moxie’s point about the ecosystem:
- Two years after the Matrix team deprecated their C implementation of Olm in favor of a Rust library, virtually all of the clients that actually switched (as of the time of my blog post disclosing vulnerabilities in their C library) were either Element, or forks of Element. The rest were still wrapping libolm.
- Most OMEMO libraries are still stuck on version 0.3.0 of the specification, and cannot communicate with XMPP+OMEMO implementations that are on newer versions of the specification.
And this is personally a vexing observation, for two reasons:
- I don’t like that Moxie’s opinion is evidently more correct when you look at the consequences of each model.
- I’m planning to develop end-to-end encryption for direct messages on the Fediverse, and don’t want to repeat the mistakes of Matrix and OMEMO.
(Aside from them mistakenly claiming to be Signal competitors, which I am not doing with my E2EE proposal or any implementations thereof.)
Fortunately, I have a solution to both annoyances that I intend to implement in my end-to-end encryption proposal.
Thus, I’d like to introduce Cryptographic Alacrity to the discussion.
Note: The term “crypto agility” was already coined by people who never learned from the alg=none vulnerability of JSON Web Tokens and think it’s A-okay to negotiate cryptographic primitives at run-time based on attacker-controllable inputs.Because they got their foolish stink all over that term, I discarded it in favor of coining a new one. I apologize for the marginal increase in cognitive load this decision may cause in the future.
Cryptographic Alacrity
For readers who aren’t already familiar with the word “alacrity” from playing Dungeons & Dragons once upon a time, the Merriam-Webster dictionary defines Alacrity as:
promptness in response : cheerful readiness
When I describe a cryptography protocol as having “cryptographic alacrity”, I mean there is a built-in mechanism to enforce protocol upgrades in a timely manner, and stragglers (read: non-compliant implementations) will lose the ability to communicate with up-to-date software.
Alacrity must be incorporated into a protocol at its design phase, specified clearly, and then enforced by the community through its protocol library implementations.
The primary difference between Alacrity and Agility is that Alacrity is implemented through protocol versions and a cryptographic mechanism for enforcing implementation freshness across the ecosystem, whereas Agility is about being able to hot-swap cryptographic primitives in response to novel cryptanalysis.
This probably still sounds a bit abstract to some of you.
To best explain what I mean, let’s look at a concrete example. Namely, how I plan on introducing Alacrity to my Fediverse E2EE design, and then enforcing it henceforth.
Alacrity in E2EE for the Fediverse
One level higher in the protocol than bulk message and/or media attachment encryption will be a Key Derivation Function. (Probably HKDF, probably as part of a Double Ratchet protocol or similar. I haven’t specified that layer just yet.)
Each invocation of HKDF will have a hard-coded 256-bit salt particular to the protocol version that is currently being used.
(What most people would think to pass as the salt in HKDF will instead be appended to the info parameter.)
The protocol version will additionally be used in a lot of other places (i.e., domain separation constants), but those are going to be predictable string values.
The salt will not be predictable until the new version is specified. I will likely tie it to the SHA256 hash of a Merkle root of a Federated Public Key Directory instance and the nickname for each protocol version.
Each library will have a small window (probably no more than 3 versions at any time) of acceptable protocol versions.
A new version will be specified, with a brand new KDF salt, every time we need to improve the protocol to address a security risk. Additionally, we will upgrade the protocol version at least once a year, even if no security risks have been found in the latest version of the protocol.
If your favorite client depends on a 4 year old version of the E2EE protocol library, you won’t be able to silently downgrade security for all of your conversation participants. Instead, you will be prevented from talking to most users, due to incompatible cryptography.
Version Deprecation Schedule
Let’s pretend, for the sake of argument, that we launch the first protocol version on January 1, 2025. And that’s when the first clients start to be built atop the libraries that speak the protocols.
Assuming no emergencies occur, after 9 months (i.e., by October 1, 2025), version 2 of the protocol will be specified. Libraries will be updated to support reading (but not sending) messages encrypted with protocol v2.
Then, on January 1, 2026 at midnight UTC–or a UNIX timestamp very close to this, at least–clients will start speaking protocol v2. Other clients can continue to read v1, but they should write v2.
This will occur every year on the same cadence, but with a twist: After clients are permitted to start writing v3, support for reading v1 MUST be removed from the codebase.
This mechanism will hold true even if the protocols are largely the same, aside from tweaked constants.
What does Alacrity give us?
Alacrity allows third-party open source developers the capability of writing their own software (both clients and servers) without a significant risk of the Matrix and OMEMO problem (i.e., stale software being used years after it should have been deprecated).
By offering a sliding window of acceptable versions and scheduling planned version bumps to be about a year apart, we can minimize the risk of clock skew introducing outages.
Additionally, it provides third-party developers ample opportunity to keep their client software conformant to the specification.
It doesn’t completely eliminate the possibility of stale versions being used in silos. Especially if some developers choose malice. However, anyone who deviates from the herd to form their own cadre of legacy protocol users has deliberately or negligently accepted the compatibility risks.
Can you staple Alacrity onto other end-to-end encryption projects?
Not easily, no.
This is the sort of mechanism that needs to be baked in from day one, and everyone needs to be onboard at the project’s inception.
Retroactively trying to make Matrix, XMPP, OpenPGP, etc. have Cryptographic Alacrity after the horses left the barn is an exercise in futility.
I would like your help introducing Alacrity into my pet project.
I’d love to help, but I’m already busy enough with work and my own projects.
If you’re designing a product that you intend to sell to the public, talk to a cryptography consulting firm. I can point you to several that are reputable, but most of them are pretty good.
If you’re designing something for the open source community, and don’t have the budget to hire professionals, I’ll respond to such inquiries when my time, energy, and emotional bandwidth is available to do so. No promises on a timeline, of course.
How do you force old versions to get dropped?
You don’t.
The mechanism I mostly care about is forcing new versions get adopted.
Dropping support for older versions is difficult to mechanize. Being actively involved in the community to encourage implementations do this (if for no other reason to reduce risk by deleting dead code) is sufficient.
I am choosing to not make perfect the enemy of good with this proposal.
This isn’t a new idea.
No, it isn’t a new idea. The privacy-focused cryptocurrency, Zcash, has a similar mechanism build into their network upgrades.
It’s wildly successful when federated or decentralized systems adopt such a policy, and actually enforce it.
The only thing that’s novel in this post is the coined term, Cryptographic Alacrity.
Addendum – Questions Asked After This Post Went Live
Art: ScruffKerfluff
What about Linux Distros with slow release cycles?
What about them?!
In my vision of the future, the primary deliverable that users will actually hold will most likely be a Browser Extension, not a binary blob signed by their Linux distro.
They already make exceptions to their glacial release cadences for browsers, so I don’t anticipate whatever release cadence we settle on being a problem in practice.
For people who write clients with desktop software: Debian and Ubuntu let users install PPAs. Anyone who does Node.js development on Linux is familiar with them.
Why 1 year?
It was an example. We could go shorter or longer depending on the needs of the ecosystem.
How will you enforce the removal of old versions if devs don’t comply?
That’s a much lower priority than enforcing the adoption of new versions.
But realistically, sending pull requests to remove old versions would be the first step.
Publicly naming and shaming clients that refuse to drop abandoned protocol versions is always an option for dealing with obstinance.
We could also fingerprint clients that still support the old versions and refuse to connect to them at all, even if there is a version in common, until they update to drop the old version.
That said, I would hope to not need to go that far.
I really don’t want to overindex on this, but people keep asking or trying to send “what about?” comments that touch on this question, so now I’m writing a definitive statement to hopefully quell this unnecessary discourse.
The ubiquitous adoption of newer versions is a much higher priority than the sunsetting of old versions. It should be obvious that getting your users to use the most secure mode available is intrinsically a net-positive.
If your client can negotiate in the most secure mode available (i.e., if we move onto post-quantum cryptography), and your friends’ clients enforce the correct minimum version, it doesn’t really matter so much if your client in particular is non-compliant.
Focusing so much on this aspect is a poor use of time and emotional bandwidth.
Header art also made by AJ.
https://soatok.blog/2024/08/28/introducing-alacrity-to-federated-cryptography/
#cryptographicAgility #cryptographicAlacrity #cryptography #endToEndEncryption #fediverse #Matrix #OMEMO #XMPP
I don’t consider myself exceptional in any regard, but I stumbled upon a few cryptography vulnerabilities in Matrix’s Olm library with so little effort that it was nearly accidental.It should not be this easy to find these kind of issues in any product people purportedly rely on for private messaging, which many people evangelize incorrectly as a Signal alternative.
Later, I thought I identified an additional vulnerability that would have been much worse, but I was wrong about that one. For the sake of transparency and humility, I’ll also describe that in detail.
This post is organized as follows:
- Disclosure Timeline
- Vulnerabilities in Olm (Technical Details)
- Recommendations
- Background Information
- An Interesting Non-Issue That Looked Critical
I’ve opted to front-load the timeline and vulnerability details to respect the time of busy security professionals.
Please keep in mind that this website is a furry blog, first and foremost, that sometimes happens to cover security and cryptography topics.Many people have, over the years, assumed the opposite and commented accordingly. The ensuing message board threads are usually is a waste of time and energy for everyone involved. So please adjust your expectations.
Art by Harubaki
If you’re curious, you can learn more here.
Disclosure Timeline
- 2024-05-15: I took a quick look at the Matrix source code. I identified two issues and emailed them to their
security@
email address.
In my email, I specify that I plan to disclose my findings publicly in 90 days (i.e. on August 14), in adherence with industry best practices for coordinated disclosure, unless they request an extension in writing.- 2024-05-16: I checked something else on a whim and find a third issue, which I also email to their
security@
email address.- 2024-05-17: Matrix security team confirms receipt of my reports.
- 2024-05-17: I follow up with a suspected fourth finding–the most critical of them all. They point out that it is not actually an issue, because I overlooked an important detail in how the code is architected. Mea culpa!
- 2024-05-18: A friend discloses a separate finding with Matrix: Media can be decrypted to multiple valid plaintexts using different keys and Malicious homeservers can trick Element/Schildichat into revealing links in E2EE rooms.
They instructed the Matrix developers to consult with me if they needed cryptography guidance. I never heard from them on this externally reported issue.- 2024-07-12: I shared this blog post draft with the Matrix security team while reminding them of the public disclosure date.
- 2024-07-31: Matrix pushes a commit that announces that libolm is deprecated.
- 2024-07-31: I email the Matrix security team asking if they plan to fix the reported issues (and if not, if there’s any other reason I should withhold publication).
- 2024-07-31: Matrix confirms they will not fix these issues (due to its now deprecated status), but ask that I withhold publication until the 14th as originally discussed.
- 2024-08-14: This blog post is publicly disclosed to the Internet.
- 2024-08-14: The lead Matrix dev claims they already knew about these issues, and, in fact, knowingly shipped cryptography code that was vulnerable to side-channel attacks for years. See Addendum.
- 2024-08-23: MITRE has assigned CVE IDs to these three findings.
Vulnerabilities in Olm
I identified the following issues with Olm through a quick skim of their source code on Gitlab:
- AES implementation is vulnerable to cache-timing attacks
- Ed25519 signatures are malleable
- Timing leakage in base64 decoding of private key material
This is sorted by the order in which they were discovered, rather than severity.
AES implementation is vulnerable to cache-timing attacks
a.k.a. CVE-2024-45191Olm ships a pure-software implementation of AES, rather than leveraging hardware acceleration.
// Substitutes a word using the AES S-Box.WORD SubWord(WORD word){unsigned int result;result = (int)aes_sbox[(word >> 4) & 0x0000000F][word & 0x0000000F];result += (int)aes_sbox[(word >> 12) & 0x0000000F][(word >> 8) & 0x0000000F] << 8;result += (int)aes_sbox[(word >> 20) & 0x0000000F][(word >> 16) & 0x0000000F] << 16;result += (int)aes_sbox[(word >> 28) & 0x0000000F][(word >> 24) & 0x0000000F] << 24;return(result);}
The code in question is called from this code, which is in turn used to actually encrypt messages.
Software implementations of AES that use a look-up table for the SubWord step of the algorithm are famously susceptible to cache-timing attacks.
This kind of vulnerability in software AES was previously used to extract a secret key from OpenSSL and dm-crypt in about 65 milliseconds. Both papers were published in 2005.
A general rule in cryptography is, “attacks only get better; they never get worse“.
As of 2009, you could remotely detect a timing difference of about 15 microseconds over the Internet with under 50,000 samples. Side-channel exploits are generally statistical in nature, so such a sample size is generally not a significant mitigation.
How is this code actually vulnerable?
In the above code snippet, the vulnerability occurs inaes_sbox[/* ... */][/* ... */]
.Due to the details of how the AES block cipher works, the input variable (
word
) is a sensitive value.Software written this way allows attackers to detect whether or not a specific value was present in one of the processor’s caches.
To state the obvious: Cache hits are faster than cache misses. This creates an observable timing difference.
Such a timing leak allows the attacker to learn the value that was actually stored in said cache. You can directly learn this from other processes on the same hardware, but it’s also observable over the Internet (with some jitter) through the normal operation of vulnerable software.
See also: cryptocoding’s description for table look-ups indexed by secret data.
How to mitigate this cryptographic side-channel
The correct way to solve this problem is to use hardware accelerated AES, which uses distinct processor features to implement the AES round function and side-steps any cache-timing shenanigans with the S-box.Not only is this more secure, but it’s faster and uses less energy too!
If you’re also targeting devices that don’t have hardware acceleration available, you should first use hardware acceleration where possible, but then fallback to a bitsliced implementation such as the one in Thomas Pornin’s BearSSL.
See also: the BearSSL documentation for constant-time AES.
Art by AJ
Ed25519 signatures are malleable
a.k.a. CVE-2024-45193Ed25519 libraries come in various levels of quality regarding signature validation criteria; much to the chagrin of cryptography engineers everywhere. One of those validation criteria involves signature malleability.
Signature malleability usually isn’t a big deal for most protocols, until suddenly you discover a use case where it is. If it matters, that usually that means you’re doing something with cryptocurrency.
Briefly, if your signatures are malleable, that means you can take an existing valid signature for a given message and public key, and generate a second valid signature for the same message. The utility of this flexibility is limited, and the impact depends a lot on how you’re using signatures and what properties you hope to get out of them.
For ECDSA, this means that for a given signature , a second signature is also possible (where is the order of the elliptic curve group you’re working with).
Matrix uses Ed25519, whose malleability is demonstrated between and .
This is trivially possible because S is implicitly reduced modulo the order of the curve, , which is a 253-bit number (
0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed
) and S is encoded as a 256-bit number.The Ed25519 library used within Olm does not ensure that , thus signatures are malleable. You can verify this yourself by looking at the Ed25519 verification code.
int ed25519_verify(const unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key) { unsigned char h[64]; unsigned char checker[32]; sha512_context hash; ge_p3 A; ge_p2 R; if (signature[63] & 224) { return 0; } if (ge_frombytes_negate_vartime(&A, public_key) != 0) { return 0; } sha512_init(&hash); sha512_update(&hash, signature, 32); sha512_update(&hash, public_key, 32); sha512_update(&hash, message, message_len); sha512_final(&hash, h); sc_reduce(h); ge_double_scalarmult_vartime(&R, h, &A, signature + 32); ge_tobytes(checker, &R); if (!consttime_equal(checker, signature)) { return 0; } return 1;}
This is almost certainly a no-impact finding (or low-impact at worst), but still an annoying one to see in 2024.
If you’d like to learn more, this page is a fun demo of Ed25519 malleability.
To mitigate this, I recommend implementing these checks from libsodium.
Art: CMYKat
Timing leakage in base64 decoding of private key material
a.k.a. CVE-2024-45192If you weren’t already tired of cache-timing attacks based on table look-ups from AES, the Matrix base64 code is also susceptible to the same implementation flaw.
while (pos != end) { unsigned value = DECODE_BASE64[pos[0] & 0x7F]; value <<= 6; value |= DECODE_BASE64[pos[1] & 0x7F]; value <<= 6; value |= DECODE_BASE64[pos[2] & 0x7F]; value <<= 6; value |= DECODE_BASE64[pos[3] & 0x7F]; pos += 4; output[2] = value; value >>= 8; output[1] = value; value >>= 8; output[0] = value; output += 3;}
The base64 decoding function in question is used to load the group session key, which means the attack published in this paper almost certainly applies.
How would you mitigate this leakage?
Steve Thomas (one of the judges of the Password Hashing Competition, among other noteworthy contributions) wrote some open source code a while back that implements base64 encoding routines in constant-time.The real interesting part is how it avoids a table look-up by using arithmetic (from this file):
// Base64 character set:// [A-Z] [a-z] [0-9] + /// 0x41-0x5a, 0x61-0x7a, 0x30-0x39, 0x2b, 0x2finline int base64Decode6Bits(char src){int ch = (unsigned char) src;int ret = -1;// if (ch > 0x40 && ch < 0x5b) ret += ch - 0x41 + 1; // -64ret += (((0x40 - ch) & (ch - 0x5b)) >> 8) & (ch - 64);// if (ch > 0x60 && ch < 0x7b) ret += ch - 0x61 + 26 + 1; // -70ret += (((0x60 - ch) & (ch - 0x7b)) >> 8) & (ch - 70);// if (ch > 0x2f && ch < 0x3a) ret += ch - 0x30 + 52 + 1; // 5ret += (((0x2f - ch) & (ch - 0x3a)) >> 8) & (ch + 5);// if (ch == 0x2b) ret += 62 + 1;ret += (((0x2a - ch) & (ch - 0x2c)) >> 8) & 63;// if (ch == 0x2f) ret += 63 + 1;ret += (((0x2e - ch) & (ch - 0x30)) >> 8) & 64;return ret;}
Any C library that handles base64 codecs for private key material should use a similar implementation. It’s fine to have a faster base64 implementation for non-secret data.
Worth noting: Libsodium also provides a reasonable Base64 codec.
Recommendations
These issues are not fixed in libolm.Instead of fixing libolm, the Matrix team recommends all Matrix clients adopt vodozemac.
I can’t speak to the security of vodozemac.
Art: CMYKat
But I can speak against the security of libolm, so moving to vodozemac is probably a good idea. It was audited by Least Authority at one point, so it’s probably fine.
Most Matrix clients that still depended on libolm should treat this blog as public 0day, unless the Matrix security team already notified you about these issues.
Background Information
If you’re curious about the backstory and context of these findings, read on.Otherwise, feel free to skip this section. It’s not pertinent to most audiences. The people that need to read it already know who they are.
End-to-end encryption is one of the topics within cryptography that I find myself often writing about.In 2020, I wrote a blog post covering end-to-end encryption for application developers. This was published several months after another blog I wrote covering gripes with AES-GCM, which included a shallow analysis of how Signal uses the algorithm for local storage.
In 2021, I published weaknesses in another so-called private messaging app called Threema.
In 2022, after Elon Musk took over Twitter, I joined the Fediverse and sought to build end-to-end encryption support for direct messages into ActivityPub, starting with a specification. Work on this effort was stalled while trying to solve Public Key distribution in a federated environment (which I hope to pick up soon, but I digress).
Earlier this year, the Telegram CEO started fearmongering about Signal with assistance from Elon Musk, so I wrote a blog post urging the furry fandom to move away from Telegram and start using Signal more. As I had demonstrated years prior, I was familiar with Signal’s code and felt it was a good recommendation for security purposes (even if its user experience needs significant work).
I thought that would be a nice, self-contained blog post. Some might listen, most would ignore it, but I could move on with my life.
I was mistaken about that last point.
Art by AJAn overwhelming number of people took it upon themselves to recommend or inquire about Matrix, which prompted me to hastily scribble down my opinion on Matrix so that I might copy/paste a link around and save myself a lot of headache.
Just when I thought the firehose was manageable and I could move onto other topics, one of the Matrix developers responded to my opinion post.
Thus, I decided to briefly look at their source code and see if any major or obvious cryptography issues would fall out of a shallow visual scan.
Since you’re reading this post, you already know how that ended.
Credit: CMYKat
Since the first draft of this blog post was penned, I also outlined what I mean when I say an encrypted messaging app is a Signal competitor or not, and published my opinion on XMPP+OMEMO (which people also recommend for private messaging).
Why mention all this?
Because it’s important to know that I have not audited the Olm or Megolm codebases, nor even glanced at their new Rust codebase.The fact is, I never intended to study Matrix. I was annoyed into looking at it in the first place.
My opinion of their project was already calcified by the previously discovered practically-exploitable cryptographic vulnerabilities in Matrix in 2022.
The bugs described above are the sort of thing I mentally scan for when I first look at a project just to get a feel for the maturity of the codebase. I do this with the expectation (hope, really) of not finding anything at all.
(If you want two specific projects that I’ve subjected to a similar treatment, and failed to discover anything interesting in: Signal and WireGuard. These two set the bar for cryptographic designs.)
It’s absolutely bonkers that an AES cache timing vulnerability was present in their code in 2024.
It’s even worse when you remember that I was inundated with Matrix evangelism in response to recommending furries use Signal.I’m a little outraged because of how irresponsible this is, in context.
It’s so bad that I didn’t even need to clone their git repository, let alone run basic static analysis tools locally.So if you take nothing else away from this blog post, let it be this:
There is roughly a 0% chance that I got extremely lucky in my mental
grep
and found the only cryptography implementation flaws in their source code. I barely tried at all and found these issues.I would bet money on there being more bugs or design flaws that I didn’t find, because this discovery was the result of an extremely half-assed effort to blow off steam.
Wasn’t libolm deprecated in May 2022?
The Matrix developers like to insist that their new Rust hotness “vodozemac” is what people should be using today.I haven’t looked at vodozemac at all, but let’s pretend, for the sake of argument, that its cryptography is actually secure.
(This is very likely if they turn out to be using RustCrypto for their primitives, but I don’t have the time or energy for that nerd snipe, so I’m not going to look. Least Authority did audit their Rust library, for what it’s worth, and Least Authority isn’t clownshoes.)
It’s been more than 2 years since they released vodozemac. What does the ecosystem penetration for this new library look like, in practice?
A quick survey of the various Matrix clients on GitHub says that libolm is still the most widely used cryptography implementation in the Matrix ecosystem (as of this writing):
Matrix Client Cryptography Backend https://github.com/tulir/gomuks libolm (1, 2) https://github.com/niochat/nio libolm (1, 2) https://github.com/ulyssa/iamb vodozemac (1, 2) https://github.com/mirukana/mirage libolm (1) https://github.com/Pony-House/Client libolm (1) https://github.com/MTRNord/cetirizine vodozemac (1) https://github.com/nadams/go-matrixcli none https://github.com/mustang-im/mustang libolm (1) https://github.com/marekvospel/libretrix libolm (1) https://github.com/yusdacra/icy_matrix none https://github.com/ierho/element libolm (through the python SDK) https://github.com/mtorials/cordless none https://github.com/hwipl/nuqql-matrixd libolm (through the python SDK) https://github.com/maxkratz/element-web vodozemac (1, 2, 3, 4) https://github.com/asozialesnetzwerk/riot libolm (wasm file) https://github.com/NotAlexNoyle/Versi libolm (1, 2) 3 of the 16 clients surveyed use the new vodozemac library. 10 still use libolm, and 3 don’t appear to implement end-to-end encryption at all.
If we only focus on clients that support E2EE, vodozemac has successfully been adopted by 19% of the open source Matrix clients on GitHub.
I deliberately excluded any repositories that were archived or clearly marked as “old” or “legacy” software, because including those would artificially inflate the representation of libolm. It would make for a more compelling narrative to do so, but I’m not trying to be persuasive here.
Deprecation policies are a beautiful lie. The impact of a vulnerability in Olm or Megolm is still far-reaching, and should be taken seriously by the Matrix community.
Worth calling out: this quick survey, which is based on a GitHub Topic, certainly misses other implementations. Both FluffyChat and Cinny, which were not tagged with this GitHub Topic, depend a language-specific Olm binding.These bindings in turn wrap libolm rather than the Rust replacement, vodozemac.
But the official clients…
I thought the whole point of choosing Matrix over something like Signal is to be federated, and run your own third-party clients?If we’re going to insist that everyone should be using Element if they want to be secure, that defeats the entire marketing point about third-party clients that Matrix evangelists cite when they decry Signal’s centralization.
So I really don’t want to hear it.
An Interesting Non-Issue That Looked Critical
As I mentioned in the timeline at the top, I thought I found a fourth issue with Matrix’s codebase. Had I been correct, this would have been a critical severity finding that the entire Matrix ecosystem would need to melt down to remediate.Fortunately for everyone, I made a mistake, and there is no fourth vulnerability after all.
However, I thought it would be interesting to write about what I thought I found, the impact it would have had if it were real, and why I believed it to be an issue.
Let’s start with the code in question:
void ed25519_sign(unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key, const unsigned char *private_key) { sha512_context hash; unsigned char hram[64]; unsigned char r[64]; ge_p3 R; sha512_init(&hash); sha512_update(&hash, private_key + 32, 32); sha512_update(&hash, message, message_len); sha512_final(&hash, r); sc_reduce(r); ge_scalarmult_base(&R, r); ge_p3_tobytes(signature, &R); sha512_init(&hash); sha512_update(&hash, signature, 32); sha512_update(&hash, public_key, 32); sha512_update(&hash, message, message_len); sha512_final(&hash, hram); sc_reduce(hram); sc_muladd(signature + 32, hram, private_key, r);}
The highlighted segment is doing pointer arithmetic. This means it’s reading 32 bytes, starting from the 32nd byte in
private_key
.What’s actually happening here is:
private_key
is the SHA512 hash of a 256-bit seed. If you look at the function prototype, you’ll notice thatpublic_key
is a separate input.Virtually every other Ed25519 implementation I’ve ever looked at before expected users to provide a 32 byte seed followed by the public key as a single input.
This led me to believe that this
private_key + 32
pointer arithmetic was actually using the public key for calculatingr
.The variable
r
(not to be confused with big R) generated via the first SHA512 is the nonce for a given signature, it must remain secret for Ed25519 to remain secure.If
r
is known to an attacker, you can do some arithmetic to recover the secret key from a single signature.Because I had mistakenly believed that
r
was calculated from the SHA512 of only public inputs (the public key and message), which I must emphasize isn’t correct, I had falsely concluded that any previously intercepted signature could be used to steal user’s private keys.Credit: CMYKat
But because
private_key
was actually the full SHA512 hash of the seed, rather than the seed concatenated with the public key, this pointer arithmetic did NOT use the public key for the calculation ofr
, so this vulnerability does not exist.If the code did what I thought it did, however, this would have been a complete fucking disaster for the Matrix ecosystem. Any previously intercepted message would have allowed an attacker to recover a user’s secret key and impersonate them. It wouldn’t be enough to fix the code; every key in the ecosystem would need to be revoked and rotated.
Whew!
I’m happy to be wrong about this one, because that outcome is a headache nobody wants.
So no action is needed, right?
Well, maybe.Matrix’s library was not vulnerable, but I honestly wouldn’t put it past software developers at large to somehow, somewhere, use the public key (rather than a secret value) to calculate the EdDSA signature nonces as described in the previous section.
To that end, I would like to propose a test vector be added to the Wycheproof test suite to catch any EdDSA implementation that misuses the public key in this way.
Then, if someone else screws up their Ed25519 implementation in the exact way I thought Matrix was, the Wycheproof tests will catch it.
For example, here’s a vulnerable test input for Ed25519:
{ "should-fail": true, "secret-key": "d1d0ef849f9ec88b4713878442aeebca5c7a43e18883265f7f864a8eaaa56c1ef3dbb3b71132206b81f0f3782c8df417524463d2daa8a7c458775c9af725b3fd", "public-key": "f3dbb3b71132206b81f0f3782c8df417524463d2daa8a7c458775c9af725b3fd", "message": "Test message", "signature": "ffc39da0ce356efb49eb0c08ed0d48a1cadddf17e34f921a8d2732a33b980f4ae32d6f5937a5ed25e03a998e4c4f5910c931b31416e143965e6ce85b0ea93c09"}
A similar test vector would also be worth creating for Ed448, but the only real users of Ed448 were the authors of the xz backdoor, so I didn’t bother with that.
(None of the Project Wycheproof maintainers knew this suggestion is coming, by the way, because I was respecting the terms of the coordinated disclosure.)
Closing Thoughts
Despite finding cryptography implementation flaws in Matric’s Olm library, my personal opinion on Matrix remains largely unchanged from 2022. I had already assumed it would not meet my bar for security.Cryptography engineering is difficult because the vulnerabilities you’re usually dealing with are extremely subtle. (Here’s an unrelated example if you’re not convinced of this general observation.) As SwiftOnSecurity once wrote:
https://twitter.com/SwiftOnSecurity/status/832058185049579524
The people that developed Olm and Megolm has not proven themselves ready to build a Signal competitor. In balance, most teams are not qualified to do so.
I really wish the Matrix evangelists would accept this and stop trying to cram Matrix down other people’s throats when they’re talking about problems with other platforms entirely.
More important for the communities of messaging apps:You don’t need to be a Signal competitor. Having E2EE is a good thing on its own merits, and really should be table stakes for any social application in 2024.
It’s only when people try to advertise their apps as a Signal alternative (or try to recommend it instead of Signal), and offer less security, that I take offense.
Just be your own thing.
My work-in-progress proposal to bring end-to-end encryption to the Fediverse doesn’t aim to compete with Signal. It’s just meant to improve privacy, which is a good thing to do on its own merits.
If I never hear Matrix evangelism again after today, it would be far too soon.If anyone feels like I’m picking on Matrix, don’t worry: I have far worse things to say about Telegram, Threema, XMPP+OMEMO, Tox, and a myriad other projects that are hungry for Signal’s market share but don’t measure up from a cryptographic security perspective.
If Signal fucked up as bad as these projects, my criticism of Signal would be equally harsh. (And remember, I have looked at Signal before.)
Addendum (2024-08-14)
One of the lead Matrix devs posted a comment on Hacker News after this blog post went live that I will duplicate here:the author literally picked random projects from github tagged as matrix, without considering their prevalence or whether they are actually maintained etc.if you actually look at % of impacted clients, it’s tiny.
meanwhile, it is very unclear that any sidechannel attack on a libolm based client is practical over the network (which is why we didn’t fix this years ago). After all, the limited primitives are commented on in the readme and https://github.com/matrix-org/olm/issues/3 since day 1.
So the Matrix developers already knew about these vulnerabilities, but deliberately didn’t fix them, for years.Congratulations, you’ve changed my stance. It used to be “I don’t consider Matrix a Signal alternative and they’ve had some embarrassing and impactful crypto bugs but otherwise I don’t care”. Now it’s a stronger stance:
Don’t use Matrix.
I had incorrectly assumed ignorance, when it was in fact negligence.
There’s no reasonable world in which anyone should trust the developers of cryptographic software (i.e., libolm) that deliberately ships with side-channels for years, knowing they’re present, and never bother to fix them.
This is fucking clownshoes.
https://soatok.blog/2024/08/14/security-issues-in-matrixs-olm-library/
#crypto #cryptography #endToEndEncryption #Matrix #sideChannels #vuln
In late 2022, I blogged about the work needed to develop a specification for end-to-end encryption for the fediverse. I sketched out some of the key management components on GitHub, and then the public work abruptly stalled.
A few of you have wondered what’s the deal with that.
This post covers why this effort stalled, what I’m proposing we do next.
What’s The Hold Up?
The “easy” (relatively speaking) parts of the problem are as follows:
- Secret key management. (This is sketched out already, and provides multiple mechanisms for managing secret key material. Yay!)
- Bulk encryption of messages and media. (I’ve done a lot of work in this space over the years, so it’s an area I’m deeply familiar with. When we get to this part, it will be almost trivial. I’m not worried about it at all.)
- Forward-secure ratcheting / authenticated key exchange / group key agreement. (RFC 9420 is a great starting point.)
That is to say, managing secret keys, using secret keys, and deriving shared secret keys are all in the “easy” bucket.
The hard part? Public key management.
CMYKat made this
Why is Public Key Management Hard?
In a centralized service (think: Twitter, Facebook, etc.), this is actually much easier to build: Shove your public keys into a database, and design your client-side software to trust whatever public key your server gives them. Bob’s your uncle, pack it up and go home.
Unfortunately, it’s kind of stupid to build anything that way.
If you explicitly trust the server, the server could provide the wrong public key (i.e., one for which the server knows the corresponding secret key) and you’ll be none the wiser. This makes it trivial for the server to intercept and read your messages.
If your users are trusting you regardless, they’re probably just as happy if you don’t encrypt at the endpoint at all (beyond using TLS, but transport encryption is table stakes for any online service so nevermind that).
But let’s say you wanted to encrypt between peers anyway, because you’re feeling generous (or don’t want to field a bunch of questionably legal demands for user data by law enforcement; a.k.a. the Snapchat threat model).
You could improve endpoint trust by shoving all of your users’ public keys into an append-only data structure; i.e. key transparency, like WhatsApp proposed in 2023:
https://www.youtube.com/watch?v=_N4Q05z5vPE
And, to be perfectly clear, key transparency is a damn good idea.
Key transparency keeps everyone honest and makes it difficult for criminals to secretly replace a victim’s public key, because the act of doing so is unavoidably published to an append-only log.
The primary challenge is scaling a transparency feature to serve a public, federated system.
Federated Key Transparency?
Despite appearances, I haven’t been sitting on my thumbs for the past year or so. I’ve been talking with cryptography experts about their projects and papers in the same space.
Truthfully, I had been hoping to piggyback off one of those upcoming projects (which is focused more on public key discovery for SAML- and OAuth-like protocols) to build the Federated PKI piece for E2EE for the Fediverse.
Unfortunately, that project keeps getting delayed and pushed back, and I’ve just about run out of patience for it.
Additionally, there are some engineering challenges that I would need to tackle to build atop it, so it’s not as simple as “let’s just use that protocol”, either.
So let’s do something else instead:
Art: ScruffKerfluff
Fediverse Public Key Directories
Orthogonal to the overall Fediverse E2EE specification project, let’s build a Public Key Directory for the Fediverse.
This will not only be useful for building a coherent specification for E2EE (as it provides the “Federated PKI” component we’d need to build it securely), but it would also be extremely useful for software developers the whole world over.
Imagine this:
- If you want to fetch a user’s SSH public key, you can just query for their username and get a list of non-expired, non-revoked public keys to choose from.
- If you wanted public key pinning and key rotation for OAuth2 and/or OpenID Connect identity providers without having to update configurations or re-deploy any applications, you can do that.
- If you want to encrypt a message to a complete stranger, such that only they can decrypt it, without any sort of interaction (i.e., they could be offline for a holiday and still decrypt it when they get back), you could do that.
Oh, and best of all? You can get all these wins without propping up any cryptocurrency bullshit either.
From simple abstractions, great power may bloom.Mark Miller
How Will This Work?
We need to design a specific kind of server that speaks a limited set of the ActivityPub protocol.
I say “limited” because it will only not support editing or deleting messages provided by another instance. It will only append data.
To understand the full picture, let’s first look at the message types, public key types, and how the message types will be interpreted.
Message Types
Under the ActivityPub layer, we will need to specify a distinct set of Directory Message Types. An opening offer would look like this:
[b]AddKey[/b]
— contains an Asymmetric Public Key, a number mapped to the user, and instance that hosts it, and some other metadata (i.e., time)[b]RevokeKey[/b]
— marks an existing public key as revoked[b]MoveIdentity[/b]
— moves all of the public keys from identity A to identity B. This can be used for username changes or instance migrations.
We may choose to allow more message types at the front-end if need be, but that’s enough for our purposes.
Public Key Types
We are not interested in backwards compatibility with every existing cryptosystem. We will only tolerate a limited set of public key types.
At the outset, only Ed25519 will be supported.
In the future, we will include post-quantum digital signature algorithms on this list, but not before the current designs have had time to mature.
RSA will never be included in the set.
ECDSA over NIST P-384 may be included at some point, if there’s sufficient interest in supporting e.g., US government users.
If ECDSA is ever allowed, RFC 6979 is mandatory.
Message Processing
When an instance sends a message to a Directory Server, it will need to contain a specific marker for our protocol. Otherwise, it will be rejected.
Each message will have its own processing rules.
After the processing rules are applied, the message will be stored in the Directory Server, and a hash of the message will be published to a SigSum transparency ledger. The Merkle root and inclusion proofs will be stored in an associated record, attached to the record for the new message.
Every message will have its hash published in SigSum. No exceptions.
We will also need a mechanism for witness co-signatures to be published and attached to the record.
Additionally, all messages defined here are generated by the users, client-side. Servers are not trusted, generally, as part of the overall E2EE threat model.
AddKey
{ "@context": "https://example.com/ns/fedi-e2ee/v1", "action": "AddKey", "message": { "time": "2024-12-31T23:59:59Z", "identity": "foo@mastodon.example.com", "public-key": "ed25519:<key goes here>" }, "signature": "SignatureOfMessage"}
The first AddKey
for any given identity will need to be self-signed by the key being added (in addition to ActivityPub messages being signed by the instance).
After an identity exists in the directory, every subsequent public key MUST be signed by a non-revoked keypair.
RevokeKey
{ "@context": "https://example.com/ns/fedi-e2ee/v1", "action": "RevokeKey", "message": { "time": "2024-12-31T23:59:59Z", "identity": "foo@mastodon.example.com", "public-key": "ed25519:<key goes here>" }, "signature": "SignatureOfMessage"}
This marks the public key as untrusted, and effectively “deletes” it from the list that users will fetch.
Important: RevokeKey will fail unless there is at least one more trusted public key for this user. Otherwise, a denial of service would be possible.
Replaying an AddKey for a previously-revoked key MUST fail.
MoveIdentity
{ "@context": "https://example.com/ns/fedi-e2ee/v1", "action": "MoveIdentity", "message": { "time": "2024-12-31T23:59:59Z", "old-identity": "foo@mastodon.example.com", "new-identity": "bar@akko.example.net" }, "signature": "SignatureOfMessage"}
This exists to facilitate migrations and username changes.
Other Message Types
The above list is not exhaustive. We may need other message types depending on the exact feature set needed by the final specification.
Fetching Public Keys
A simple JSON API (and/or an ActivityStream; haven’t decided) will be exposed to query for the currently trusted public keys for a given identity.
{ "@context": "https://example.com/ns/fedi-e2ee/v1", "public-keys": [ { "data": { "time": "2024-12-31T23:59:59Z", "identity": "foo@mastodon.example.com", "public-key": "ed25519:<key goes here>" }, "signature": "SignatureOfData", "sigsum": { /* ... */ }, }, { "data": { /* ... */ }, /* ... */ }, /* ... */ ]}
Simple and easy.
Gossip Between Instances
Directory Servers should be configurable to mirror records from other instances.
Additionally, they should be configurable to serve as Witnesses for the SigSum protocol.
The communication layer here between Directory Servers will also be ActivityPub.
Preventing Abuse
The capability of learning a user’s public key doesn’t imply the ability to send messages or bypass their block list.
Additionally, Fediverse account usernames are (to my knowledge) generally not private, so I don’t anticipate there being any danger in publishing public keys to an append-only ledger.
That said, I am totally open to considering use cases where the actual identity is obfuscated (e.g., HMAC with a static key known only to the instance that hosts them instead of raw usernames).
What About GDPR / Right To Be Forgotten?
Others have previously suggested that usernames might be subject to the “right to be forgotten”, which would require breaking history for an append-only ledger.
After discussing a proposed workaround with a few people in the Signal group for this project, we realized complying necessarily introduced security issues by giving instance admins the capability of selectively remapping the user ID to different audiences, and detecting/mitigating this remapping is annoying.
However, we don’t need to do that in the first place.
According to this webpage about GDPR’s Right to be Forgotten:
However, an organization’s right to process someone’s data might override their right to be forgotten. Here are the reasons cited in the GDPR that trump the right to erasure:
- (…)
- The data is being used to perform a task that is being carried out in the public interest or when exercising an organization’s official authority.
- (…)
- The data represents important information that serves the public interest, scientific research, historical research, or statistical purposes and where erasure of the data would likely to impair or halt progress towards the achievement that was the goal of the processing.
Enabling private communication is in the public interest. The only information that will be stored in the ledger in relation to the username are cryptographic public keys, so it’s not like anything personal (e.g., email addresses or legal names) will be included.
However, we still need to be extremely up-front about this to ensure EU citizens are aware of the trade-off we’re making.
Account Recovery
In the event that a user loses access to all of their secret keys and wants to burn down the old account, they may want a way to start over with another fresh self-signed AddKey
.
However, the existing policies I wrote above would make this challenging:
- Since every subsequent
AddKey
must be signed by an incumbent key, if you don’t have access to these secret keys, you’re locked out. - Since
RevokeKey
requires one trusted keypair remains in the set, for normal operations, you can’t just burn the set down to zero even while you still had access to the secret keys.
There is an easy way out of this mess: Create a new verb; e.g. BurnDown
that an instance can issue that resets all signing keys for a given identity.
The use of BurnDown
should be a rare, exceptional event that makes a lot of noise:
- All existing E2EE sessions must break, loudly.
- All other participants must be alerted to the change, through the client software.
- Witnesses and watchdog nodes must take note of this change.
This comes with some trade-offs. Namely: Any account recovery mechanism is a backdoor, and giving the instance operators the capability of issuing BurnDown
messages is a risk to their users.
Therefore, users who trust their own security posture and wish to opt out of this recovery feature should also be able to issue a Fireproof
message at any point in the process, which permanent and irrevocably prevents any BurnDown
from being accepted on their current instance.
If users opt out of recovery and then lose their signing keys, they’re locked out and need to start over with a new Fediverse identity. On the flipside, their instance operator cannot successfully issue a BurnDown for them, so they have to trust them less.
Notice
This is just a rough sketch of my initial ideas, going into this project. It is not comprehensive, nor complete.
There are probably big gaps that need to be filled in, esp. on the ActivityPub side of things. (I’m not as worried about the cryptography side of things.)
How Will This Be Used for E2EE Direct Messaging?
I anticipate that a small pool of Directory Servers will be necessary, due to only public keys and identities being stored.
Additional changes beyond just the existence of Directory Servers will need to be made to facilitate private messaging. Some of those changes include:
- Some endpoint for users to know which Directory Servers a given ActivityPub instance federates with (if any).
- Some mechanism for users to asynchronously exchange Signed Pre-Key bundles for initiating contact. (One for users to publish new bundles, another for users to retrieve a bundle.)
- These will be Ed25519-signed payloads containing an ephemeral X25519 public key.
This is all outside the scope of the proposal I’m sketching out here today, but it’s worth knowing that I’m aware of the implementation complexity.
The important thing is: I (soatok@furry.engineer) should be able to query pawb.fun, find the Directory Server(s) they federate with, and then query that Directory server for Crashdoom@pawb.fun
and get his currently trusted Ed25519 public keys.
From there, I can query pawb.fun for a SignedPreKey bundle, which will have been signed by one of those public keys.
And then we can return to the “easy” pile.
Development Plan
Okay, so that was a lot of detail, and yet not enough detail, depending on who’s reading this blog post.
What I wrote here today is a very rough sketch. The devil is always in the details, especially with cryptography.
Goals and Non-Goals
We want Fediverse users to be able to publish a public key that is bound to their identity, which anyone else on the Internet can fetch and then use for various purposes.
We want to leverage the existing work into key transparency by the cryptography community.
We don’t want to focus on algorithm agility or protocol compatibility.
We don’t want to involve any government offices in the process. We don’t care about “real” identities, nor about codifying falsehoods about names.
We don’t want any X.509 or Web-of-Trust machinery involved in the process.
Tasks
The first thing we would need to do is write a formal specification for a Directory Server (whose job is only to vend Public Keys in an auditable, transparent manner).
Next, we need to actually build a reference implementation of this server, test it thoroughly, and then have security experts pound at the implementation for a while. Any security issues that can be mitigated by design will require a specification update.
We will NOT punt these down to implementors to be responsible for, unless we cannot avoid doing so.
Once these steps are done, we can start rolling the Directory Servers out. At this point, we can develop client-side libraries in various programming languages to make it easy for developers to adopt.
My continued work on the E2EE specification for the Fediverse can begin after we have an implementation of the Directory Server component ready to go.
Timeline
I have a very demanding couple of months ahead of me, professionally, so I don’t yet know when I can commit to starting the Fediverse Directory Server specification work.
Strictly speaking, it’s vaguely possible to get buy-in from work to focus on this project as part of my day-to-day responsibilities, since it has immediate and lasting value to the Internet.However, I don’t want to propose it because that would be crossing the professional-personal streams in a way I’m not really comfortable with.
The last thing I need is angry Internet trolls harassing my coworkers to try to get under my fur, y’know?
If there is enough interest from the broader Fediverse community, I’m also happy to delegate this work to anyone interested.
Once the work can begin, I don’t anticipate it will take more than a week for me to write a specification that other crypto nerds will take seriously.
I am confident in this because most of the cryptography will be constrained to hash functions, preventing canonicalization and cross-protocol attacks, and signatures.
Y’know, the sort of thing I write about on my furry blog for fun!
Building a reference implementation will likely take a bit longer; if, for no other reason, than I believe it would be best to write it in Go (which has the strongest SigSum support, as of this writing).
This is a lot of words to say, as far as timelines go:
How to Get Involved
Regardless of whether my overall E2EE proposal gets adopted, the Directory Server component is something that should be universally useful to the Fediverse and to software developers around the world.
If you are interested in participating in any technical capacity, I have just created a Signal Group for discussing and coordinating efforts.
All of these efforts will also be coordinated on the fedi-e2ee GitHub organization.
The public key directory server’s specification will eventually exist in this GitHub repository.
Can I Contribute Non-Technically?
Yes, absolutely. In the immediate future, once it kicks off, the work is going to be technology-oriented.
However, we may need people with non-technical skills at some point, so feel free to dive in whenever you feel comfortable.
What About Financially?
If you really have money burning a hole in your pocket and want to toss a coin my way, I do have a Ko-Fi. Do not feel pressured at all to do so, however.
Because I only use Ko-Fi as a tip jar, rather than as a business, I’m not specifically tracking which transaction is tied to which project, so I can’t make any specific promises about how any of the money sent my way will be allocated.
What I will promise, however, is that any icons/logos/etc. created for this work will be done by an artist and they will be adequately compensated for their work. I will not use large-scale computing (a.k.a., “Generative AI”) for anything.
Closing Thoughts
What I’ve sketched here is much simpler (and more ActivityPub-centric) than the collaboration I was originally planning.
Thanks for being patient while I tried, in vain, to make that work.
As of today, I no longer think we need to wait for them. We can build this ourselves, for each other.
https://soatok.blog/2024/06/06/towards-federated-key-transparency/
#cryptography #endToEndEncryption #fediverse #KeyTransparency #Mastodon #MerkleTrees #PublicKeys
Update (2024-06-06): There is an update on this project.As Twitter’s new management continues to nosedive the platform directly into the ground, many people are migrating to what seem like drop-in alternatives; i.e. Cohost and Mastodon. Some are even considering new platforms that none of us have heard of before (one is called “Hive”).
Needless to say, these are somewhat chaotic times.
One topic that has come up several times in the past few days, to the astonishment of many new Mastodon users, is that Direct Messages between users aren’t end-to-end encrypted.
And while that fact makes Mastodon DMs no less safe than Twitter DMs have been this whole time, there is clearly a lot of value and demand in deploying end-to-end encryption for ActivityPub (the protocol that Mastodon and other Fediverse software uses to communicate).
However, given that Melon Husk apparently wants to hurriedly ship end-to-end encryption (E2EE) in Twitter, in some vain attempt to compete with Signal, I took it upon myself to kickstart the E2EE effort for the Fediverse.
https://twitter.com/elonmusk/status/1519469891455234048
So I’d like to share my thoughts about E2EE, how to design such a system from the ground up, and why the direction Twitter is heading looks to be security theater rather than serious cryptographic engineering.
If you’re not interested in those things, but are interested in what I’m proposing for the Fediverse, head on over to the GitHub repository hosting my work-in-progress proposal draft as I continue to develop it.
How to Quickly Build E2EE
If one were feeling particularly cavalier about your E2EE designs, they could just generate then dump public keys through a server they control, pass between users, and have them encrypt client-side. Over and done. Check that box.Every public key would be ephemeral and implicitly trusted, and the threat model would mostly be, “I don’t want to deal with law enforcement data requests.”
Hell, I’ve previously written an incremental blog post to teach developers about E2EE that begins with this sort of design. Encrypt first, ratchet second, manage trust relationships on public keys last.
If you’re catering to a slightly tech-savvy audience, you might throw in SHA256(pk1 + pk2) -> hex2dec() and call it a fingerprint / safety number / “conversation key” and not think further about this problem.
Look, technical users can verify out-of-band that they’re not being machine-in-the-middle attacked by our service.An absolute fool who thinks most people will ever do this
From what I’ve gathered, this appears to be the direction that Twitter is going.https://twitter.com/wongmjane/status/1592831263182028800
Now, if you’re building E2EE into a small hobby app that you developed for fun (say: a World of Warcraft addon for erotic roleplay chat), this is probably good enough.
If you’re building a private messaging feature that is intended to “superset Signal” for hundreds of millions of people, this is woefully inadequate.
https://twitter.com/elonmusk/status/1590426255018848256
Art: LvJ
If this is, indeed, the direction Musk is pushing what’s left of Twitter’s engineering staff, here is a brief list of problems with what they’re doing.
- Twitter Web. How do you access your E2EE DMs after opening Twitter in your web browser on a desktop computer?
- If you can, how do you know twitter.com isn’t including malicious JavaScript to snarf up your secret keys on behalf of law enforcement or a nation state with a poor human rights record?
- If you can, how are secret keys managed across devices?
- If you use a password to derive a secret key, how do you prevent weak, guessable, or reused passwords from weakening the security of the users’ keys?
- If you cannot, how do users decide which is their primary device? What if that device gets lost, stolen, or damaged?
- Authenticity. How do you reason about the person you’re talking with?
- Forward Secrecy. If your secret key is compromised today, can you recover from this situation? How will your conversation participants reason about your new Conversation Key?
- Multi-Party E2EE. If a user wants to have a three-way E2EE DM with the other members of their long-distance polycule, does Twitter enable that?
- How are media files encrypted in a group setting? If you fuck this up, you end up like Threema.
- Is your group key agreement protocol vulnerable to insider attacks?
- Cryptography Implementations.
- What does the KEM look like? If you’re using ECC, which curve? Is a common library being used in all devices?
- How are you deriving keys? Are you just using the result of an elliptic curve (scalar x point) multiplication directly without hashing first?
- Independent Third-Party Review.
- Who is reviewing your protocol designs?
- Who is reviewing your cryptographic primitives?
- Who is reviewing the code that interacts with E2EE?
- Is there even a penetration test before the feature launches?
As more details about Twitter’s approach to E2EE DMs come out, I’m sure the above list will be expanded with even more questions and concerns.
My hunch is that they’ll reuse liblithium (which uses Curve25519 and Gimli) for Twitter DMs, since the only expert I’m aware of in Musk’s employ is the engineer that developed that library for Tesla Motors. Whether they’ll port it to JavaScript or just compile to WebAssembly is hard to say.
How To Safely Build E2EE
You first need to decompose the E2EE problem into five separate but interconnected problems.
- Client-Side Secret Key Management.
- Multi-device support
- Protect the secret key from being pilfered (i.e. by in-browser JavaScript delivered from the server)
- Public Key Infrastructure and Trust Models.
- TOFU (the SSH model)
- X.509 Certificate Authorities
- Certificate/Key/etc. Transparency
- SigStore
- PGP’s Web Of Trust
- Key Agreement.
- While this is important for 1:1 conversations, it gets combinatorially complex when you start supporting group conversations.
- On-the-Wire Encryption.
- Direct Messages
- Media Attachments
- Abuse-resistance (i.e. message franking for abuse reporting)
- The Construction of the Previous Four.
- The vulnerability of most cryptographic protocols exists in the joinery between the pieces, not the pieces themselves. For example, Matrix.
This might not be obvious to someone who isn’t a cryptography engineer, but each of those five problems is still really hard.
To wit: The latest IETF RFC draft for Message Layer Security, which tackles the Key Agreement problem above, clocks in at 137 pages.
Additionally, the order I specified these problems matters; it represents my opinion of which problem is relatively harder than the others.
When Twitter’s CISO, Lea Kissner, resigned, they lost a cryptography expert who was keenly aware of the relative difficulty of the first problem.
https://twitter.com/LeaKissner/status/1592937764684980224
You may also notice the order largely mirrors my previous guide on the subject, in reverse. This is because teaching a subject, you start with the simplest and most familiar component. When you’re solving problems, you generally want the opposite: Solve the hardest problems first, then work towards the easier ones.
This is precisely what I’m doing with my E2EE proposal for the Fediverse.
The Journey of a Thousand Miles Begins With A First Step
Before you write any code, you need specifications.Before you write any specifications, you need a threat model.
Before you write any threat models, you need both a clear mental model of the system you’re working with and how the pieces interact, and a list of security goals you want to achieve.
Less obviously, you need a specific list of non-goals for your design: Properties that you will not prioritize. A lot of security engineering involves trade-offs. For example: elliptic curve choice for digital signatures is largely a trade-off between speed, theoretical security, and real-world implementation security.
If you do not clearly specify your non-goals, they still exist implicitly. However, you may find yourself contradicting them as you change your mind over the course of development.
Being wishy-washy about your security goals is a good way to compromise the security of your overall design.
In my Mastodon E2EE proposal document, I have a section called Design Tenets, which states the priorities used to make trade-off decisions. I chose Usability as the highest priority, because of AviD’s Rule of Usability.
Security at the expense of usability comes at the expense of security.Avi Douglen, Security StackExchange
Underneath Tenets, I wrote Anti-Tenets. These are things I explicitly and emphatically do not want to prioritize. Interoperability with any incumbent designs (OpenPGP, Matrix, etc.) is the most important anti-tenet when it comes to making decisions. If our end-state happens to interop with someone else’s design, cool. I’m not striving for it though!Finally, this section concludes with a more formal list of Security Goals for the whole project.
Art: LvJ
Every component (from the above list of five) in my design will have an additional dedicated Security Goals section and Threat Model. For example: Client-Side Secret Key Management.
You will then need to tackle each component independently. The threat model for secret-key management is probably the trickiest. The actual encryption of plaintext messages and media attachments is comparatively simple.
Finally, once all of the pieces are laid out, you have the monumental (dare I say, mammoth) task of stitching them together into a coherent, meaningful design.
If you did your job well at the outset, and correctly understand the architecture of the distributed system you’re working with, this will mostly be straightforward.
Making Progress
At every step of the way, you do need to stop and ask yourself, “If I was an absolute chaos gremlin, how could I fuck with this piece of my design?” The more pieces your design has, the longer the list of ways to attack it will grow.It’s also helpful to occasionally consider formal methods and security proofs. This can have surprising implications for how you use some algorithms.
You should also be familiar enough with the cryptographic primitives you’re working with before you begin such a journey; because even once you’ve solved the key management story (problems 1, 2 and 3 from the above list of 5), cryptographic expertise is still necessary.
- If you’re feeding data into a hash function, you should also be thinking about domain separation. More information.
- If you’re feeding data into a MAC or signature algorithm, you should also be thinking about canonicalization attacks. More information.
- If you’re encrypting data, you should be thinking about multi-key attacks and confused deputy attacks. Also, the cryptographic doom principle if you’re not using IND-CCA3 algorithms.
- At a higher-level, you should proactively defend against algorithm confusion attacks.
How Do You Measure Success?
It’s tempting to call the project “done” once you’ve completed your specifications and built a prototype, and maybe even published a formal proof of your design, but you should first collect data on every important metric:
- How easy is it to use your solution?
- How hard is it to misuse your solution?
- How easy is it to attack your solution? Which attackers have the highest advantage?
- How stable is your solution?
- How performant is your solution? Are the slow pieces the deliberate result of a trade-off? How do you know the balance was struck corectly?
Where We Stand Today
I’ve only begun writing my proposal, and I don’t expect it to be truly ready for cryptographers or security experts to review until early 2023.However, my clearly specified tenets and anti-tenets were already useful in discussing my proposal on the Fediverse.
@soatok @fasterthanlime Should probably embed the algo used for encryption in the data used for storing the encrypted blob, to support multiples and future changes.@fabienpenso@hachyderm.io proposes in-band protocol negotiation instead of versioned protocols
The main things I wanted to share today are:
- The direction Twitter appears to be heading with their E2EE work, and why I think it’s a flawed approach
- Designing E2EE requires a great deal of time, care, and expertise; getting to market quicker at the expense of a clear and careful design is almost never the right call
Mastodon? ActivityPub? Fediverse? OMGWTFBBQ!
In case anyone is confused about Mastodon vs ActivityPub vs Fediverse lingo:The end goal of my proposal is that I want to be able to send DMs to queer furries that use Mastodon such that only my recipient can read them.
Achieving this end goal almost exclusively requires building for ActivityPub broadly, not Mastodon specifically.
However, I only want to be responsible for delivering this design into the software I use, not for every single possible platform that uses ActivityPub, nor all the programming languages they’re written in.
I am going to be aggressive about preventing scope creep, since I’m doing all this work for free. (I do have a Ko-Fi, but I won’t link to it from here. Send your donations to the people managing the Mastodon instance that hosts your account instead.)
My hope is that the design documents and technical specifications become clear enough that anyone can securely implement end-to-end encryption for the Fediverse–even if special attention needs to be given to the language-specific cryptographic libraries that you end up using.
Art: LvJ
Why Should We Trust You to Design E2EE?
This sort of question comes up inevitably, so I’d like to tackle it preemptively.My answer to every question that begins with, “Why should I trust you” is the same: You shouldn’t.
There are certainly cryptography and cybersecurity experts that you will trust more than me. Ask them for their expert opinions of what I’m designing instead of blanketly trusting someone you don’t know.
I’m not interested in revealing my legal name, or my background with cryptography and computer security. Credentials shouldn’t matter here.
If my design is good, you should be able to trust it because it’s good, not because of who wrote it.
If my design is bad, then you should trust whoever proposes a better design instead. Part of why I’m developing it in the open is so that it may be forked by smarter engineers.
Knowing who I am, or what I’ve worked on before, shouldn’t enter your trust calculus at all. I’m a gay furry that works in the technology industry and this is what I’m proposing. Take it or leave it.
Why Not Simply Rubber-Stamp Matrix Instead?
(This section was added on 2022-11-29.)There’s a temptation, most often found in the sort of person that comments on the /r/privacy subreddit, to ask why even do all of this work in the first place when Matrix already exists?
The answer is simple: I do not trust Megolm, the protocol designed for Matrix.
Megolm has benefited from amateur review for four years. Non-cryptographers will confuse this observation with the proposition that Matrix has benefited from peer review for four years. Those are two different propositions.
In fact, the first time someone with cryptography expertise bothered to look at Matrix for more than a glance, they found critical vulnerabilities in its design. These are the kinds of vulnerabilities that are not easily mitigated, and should be kept in mind when designing a new protocol.
You don’t have to take my word for it. Listen to the Security, Cryptography, Whatever podcast episode if you want cryptographic security experts’ takes on Matrix and these attacks.
From one of the authors of the attack paper:
So they kind of, after we disclosed to them, they shared with us their timeline. It’s not fixed yet. It’s a, it’s a bigger change because they need to change the protocol. But they always said like, Okay, fair enough, they’re gonna change it. And they also kind of announced a few days after kind of the public disclosure based on the public reaction that they should prioritize fixing that. So it seems kind of in the near future, I don’t have the timeline in front of me right now. They’re going to fix that in the sense of like the— because there’s, notions of admins and so on. So like, um, so authenticating such group membership requests is not something that is kind of completely outside of, kind of like the spec. They just kind of need to implement the appropriate authentication and cryptography.Martin Albrecht, SCW podcast
From one of the podcast hosts:I guess we can at the very least tell anyone who’s going forward going to try that, that like, yes indeed. You should have formal models and you should have proofs. And so there’s this, one of the reactions to kind of the kind of attacks that we presented and also to prior previous work where we kind of like broken some cryptographic protocols is then to say like, “Well crypto’s hard”, and “don’t roll your own crypto.” But in a way the thing is like, you know, we need some people to roll their own crypto because that’s how we have crypto. Someone needs to roll it. But we have developed techniques, we have developed formalisms, we have developed methods for making sure it doesn’t have to be hard, it’s not, it’s not a dark art kind of that only kind of a few, a select few can master, but it’s, you know, it’s a science and you can learn it. So, but you need to then indeed employ a cryptographer in kind of like forming, modeling your protocol and whenever you make changes, then, you know, they need to look over this and say like, Yes, my proof still goes through. Um, so like that is how you do this. And then, then true engineering is still hard and it will remain hard and you know, any science is hard, but then at least you have some confidence in what you’re doing. You might still then kind of on the space and say like, you know, the attack surface is too large and I’m not gonna to have an encrypted backup. Right. That’s then the problem of a different hard science, social science. Right. But then just use the techniques that we have, the methods that we have to establish what we need.Thomas Ptacek, SCW podcast
It’s tempting to listen to these experts and say, “OK, you should use libsignal instead.”But libsignal isn’t designed for federation and didn’t prioritize group messaging. The UX for Signal is like an IM application between two parties. It’s a replacement for SMS.
It’s tempting to say, “Okay, but you should use MLS then; never roll your own,” but MLS doesn’t answer the group membership issue that plagued Matrix. It punts on these implementation details.
Even if I use an incumbent protocol that privacy nerds think is good, I’ll still have to stitch it together in a novel manner. There is no getting around this.
Maybe wait until I’ve finished writing the specifications for my proposal before telling me I shouldn’t propose anything.
Credit for art used in header: LvJ, Harubaki
https://soatok.blog/2022/11/22/towards-end-to-end-encryption-for-direct-messages-in-the-fediverse/
If you’re new to reading this blog, you might not already be aware of my efforts to develop end-to-end encryption for ActivityPub-based software. It’s worth being aware of before you continue to read this blog post.
To be very, very clear, this is work I’m doing independent of the W3C or any other standards organization and/or funding source (and they have their own ideas about how to approach it).Really, I’m doing my own thing and releasing my designs under a public domain-equivalent license so anyone (including the W3C grant awardees) can pick it up and use it, if they see fit.
But the work I’m doing has no official standing and is not representative of anyone (except maybe a lot of other furries interested in technology). They have, emphatically, never endorsed anything I’m doing. I have not talked with any of them about my ideas, nor has my name come up in any of their meeting notes.
My background is in applied cryptography and software security assessments, so I have strong opinions about how such software should be developed.
I’m being very up-front about this because I don’t want anyone to mistake my ideas for anything “official”.
Why spend your time on that?
My end goal is pretty straightforward.
Before Musk took it over, Twitter was wonderful for queer people. I’ve even heard it described as the most successful dating platform for the LGBTQIA+ community.
These days, it’s full of Nazis and people who think the ideal version of “free speech” means not being allowed to say the word “cisgender.” But I repeat myself.
The typical threat model for Twitter was: You have to trust the person you’re talking with, and the Twitter corporation, to keep your conversations (or nudes, if we’re being frank about it) private.
With the Fediverse, things are a little more complicated. Instance operators also have access to the plaintext versions of any Direct Messages between you and other participants.
And maybe you trust your instance operator… but do you trust your friends’? And do they trust yours?
If implemented securely, end-to-end encryption saves you from having to care about this injection of additional threat actors to consider.
If not implemented securely, it’s little more than security theater and should be ridiculed loudly.
So it’s natural and obvious for a person with my particular interests and skills to want to solve this problem.
Technological Decisions
When I started this project, I separated the end goal into 4 separate components:
- Client-side secret key management.
- Federated public-key infrastructure.
- Shared key agreement for group messaging.
- The actual bulk encryption techniques.
A lot of hobbyist projects over-index on the fourth component, rather than the actual hard problems. This is why so many doomed projects start with PGP, or implement weird “cipher cascades” to hedge against AES getting broken.
In reality, every component matters for the security of the whole system, but the bulk encryption is boring. It’s the well-tread path of any cryptosystem. The significantly harder parts are key management.
Political Decisions
Let’s not mince words: How you implement key management is inherently a political decision.
If that sounds counter-intuitive, meditate on this bit of wisdom for a while:
Repeat after me: all technical problems of sufficient scope or impact are actually political problems first.
Many projects, when confronted with the complexity of key management, are perfectly happy with “just write private keys to disk” or “put blind trust in AWS KMS.”
Or, more directly: “YOLO.”
With my Fediverse E2EE project, I wanted to minimize the amount of trust you have to place in others. (Especially, minimize the trust needed in Soatok!)
How Decisions Flow
Client-side secrets are the most visible area of risk to end users. Backing up and managing their own credentials, recovering from failure modes, the Mud Puddle test, etc.
Once each participant has secret keys managed (1), they can provide public keys to each other.
Public-key infrastructure (2) is how you decide trust relationships between parties. We’re operating in a federated environment, and want to minimize the amount of unchecked “authority” anyone has, so that complicates matters. But, if it wasn’t challenging, it would already be solved.
Once you’ve figured out a trust mechanism to tie a public key to an identity, you can try to agree on a shared symmetric key securely, even over an untrusted channel.
Key agreement for group messaging (3) is how you decide which shared key to use, and when, and who has access to this key and for how long.
And from there, you can actually encrypt shit (4).
It doesn’t really matter how much you boil the ocean on mitigating hypothetical weaknesses in AES if an adversary can muck with your key management.
Thus, it should hopefully be reasonable to divide the work up in this fashion.
But there is a fifth component; one that I am not qualified to comment on:
User experience.
The final deliverable for my participation in this project will be software libraries (and any necessary patches to server software) to facilitate secure end-to-end encryption between Fediverse users.
As for what that experience looks like? How it’s presented visually? What accessibility features are used, and how? How elements are organized and in what order they are displayed? Any quality-of-life design decisions that delight users and avoid dark patterns?
Yeah, sorry, I’m totally out of my depth here. That’s not my domain.
I will do my damnedest to not make security decisions that are inherently onerous towards making usable software.
(After all, security at the cost of usability comes at the cost of security.)
But I can’t promise that the experience will be totally seamless for everyone, all the time.
Lacking Ambition?
One of the things that’s been bothering me, as I work on out the finer details about this end-to-end encryption project, is that it seems to lack ambition.
Sure, I can talk your ear off for hours about the ins and outs of implementing end-to-end encryption securely, but we already have end-to-end encryption apps. So many private messengers.
How does “you can now have encrypted DMs in Mastodon” help people who can already use Signal or WhatsApp? Why should the people who aren’t computer nerds care about it at all?
What’s actually new or exciting about this work?
And, honestly, the best answer I can come up with is that it’s the first step.
Tech Freedom and You
Before the Big Data and cloud computing crazes took the technology industry by storm (or any of the messes that followed), most software was designed to work offline. That is, without Internet access.
With the growing ubiquity of Internet access (and mobile networks), the Overton window shifted towards always-on devices, edge computing, and no longer owning anything. Instead, consumers rent licenses to software that a third party can revoke on a whim.
The Free Software movement, for all of the very pronounced personality quirks associated with it today, foresaw this problem long before the modern Internet existed. Technologists, lawyers, and activists spent thousands of person-years of effort on trying to protect end users’ rights from greedy monopolies.
Kyume
(I couldn’t not include this meme in this section.)
This isn’t a modern problem, by any stretch of the imagination.
Every year, our rights and digital freedoms are eroded by court decisions by corrupt judges, terrible legislature, and questionable leadership.
But the Electronic Frontier Foundation and its friends in other nations have been talking about this and fighting court battles since the 1990s.
Even if I somehow made some small innovation that benefited end users with allowing Fediverse users to message each other privately, that’s not really ambitious either.
From Sparks to Embers
As I was noodling over this, a friend of mine linked me to an article titled Rust Needs a Web Framework for Lazy Developers the other day.
It made me realize how much I miss the era when software was offline-first, even if it had online components. The past several years of Live Service Games has exhausted my tolerance more than anything else, but they’re not alone.
When I initially delineated my proposal into 4 components, my goal was to simplify the security analysis and make the threat models digestible.
But it occurred to me, recently, that by abstracting these components (especially the Federated Public Key Infrastructure design), a new era of cypherpunks and pirates could breathe new ambition into software projects that build atop the boring infrastructure I’m building.
Let’s Turn the Ambition Up To 11
Imagine peer-to-peer software that uses the Fediverse and/or onion routing technologies (similar to Tor) to establish peer-to-peer encrypted data tunnels between devices, with the Federated PKI as the source of truth for identity public keys so you always know you’re talking to the correct entity.
Now combine that with developer tools that make it easy for people to self-publish software (even if only through Tor Hidden Services), with an optional way to create a public portal (e.g., for a public-facing website).
You could even create a protocol for people with rack space and spare bandwidth to host said public portals, without biasing for a particular one.
This would allow technologists to build the tools for normal people to create an anti-corporate, decentralized network.
And you could do it without ever mentioning the word “blockchain” (though you may need to tolerate it if you want to prevent anti-porn groups like Exodus Cry from having any say in what we compute).
Finally, imagine that we build all of this in memory-safe languages.
Are you building this today?
In short: No, I’m not.
Ambitious ideas and cryptography should only intersect rarely. I’m focused on the cryptography.
Instead, I wanted to lay this rough sketch out there as a possibility that someone else–presumably more ambitious, charismatic, and/or resourceful–could easily pick up if they so choose.
More importantly, all of the hard parts of this would be solved problems by the time I finish with the end-to-end encryption project. (Most of them already exist, in fact!)
That’s what I meant above by “it’s the first step”.
Along the way to achieving my own goals, I’m building at least one useful building block. What the rest of the technology industry decides to do with it is up to the rest of us.
I can’t, and will not try, to do it alone.
There is a lot of potential for tech freedom that could benefit users beyond what they can get from the Fediverse today. I wanted to examine how some of these ideas could be useful for–
Rejected! What else you got?
Oh.
…
Okay, so y’know how a lot of video games (Undertale/Deltarune, Doki Doki Literature Club) try to make a highly immersive experience with many diegetic elements?
Let’s build an operating system, based on some flavor of Linux, that is in and of itself a game. People can write their own DLC by developing packages for that OS. The end deliverable will be a virtual machine, and in order to get it to work on Steam, we would install Docker or Kubernetes, but users will also be able to install it via VirtualBox.
Inevitably, someone will decide this OS is their new daily driver. Imagine the impact this would have on corporate IT the whole world over.
This is the worst idea in the history of bad ideas!
Oh, I can do worse. I can do so much worse.
I don’t know if I can top the various attempts to build a Message Authentication Code out of the insecure RC4 stream cipher, of course.
If you want ambition, you sacrifice wisdom.
If you want freedom, you sacrifice convenience.
If you want security, you sacrifice usability.
…
Or do you?
They Can’t All Be Winners
I have a lot of bad ideas, all the time. That’s the only reason I ever occasionally have moderately good ones.
My process of eliminating bad ideas is ruthless, and may cull some interesting or fun ones along the way. This is an unfortunate side-effect of being an effective security engineer.
I don’t actually think the ideas I’ve written above are that bad. I wrote them this way for comedic effect.
Rather, I’m just not actually sure they’re actually good, or worthwhile to invest time into.
Whether someone could build atop the work I’m doing to reclaim our Internet from the grip of massive technology corporations is, at best, difficult to classify.
I do not have the time, energy, or motivation to do the work already on my own plate and then explore these ideas fully.
Maybe someone reading this does?
If not, that’s cool. Ideas are allowed to just exist as idle curiosities. Not everything has to matter all the time.
The “ship a whole god damn OS as an indie
game” idea could be fun though.
https://soatok.blog/2024/10/12/ambition-the-fediverse-and-technology-freedom/
#endToEndEncryption #fediverse #FreeSoftware #OnlinePrivacy #Society #SoftwareFreedom #TechFreedom #Technology
In 2022, I wrote about my plan to build end-to-end encryption for the Fediverse. The goals were simple:
- Provide secure encryption of message content and media attachments between Fediverse users, as a new type of Direct Message which is encrypted between participants.
- Do not pretend to be a Signal competitor.
The primary concern at the time was “honest but curious” Fediverse instance admins who might snoop on another user’s private conversations.
After I finally was happy with the client-side secret key management piece, I had moved on to figure out how to exchange public keys. And that’s where things got complicated, and work stalled for 2 years.
Art: AJ
I wrote a series of blog posts on this complication, what I’m doing about it, and some other cool stuff in the draft specification.
- Towards Federated Key Transparency introduced the Public Key Directory project
- Federated Key Transparency Project Update talked about some of the trade-offs I made in this design
- Not supporting ECDSA at all, since FIPS 186-5 supports Ed25519
- Adding an account recovery feature, which power users can opt out of, that allows instance admins to help a user recover from losing all their keys
- Building a Key Transparency system that can tolerate GDPR Right To Be Forgotten takedown requests without invalidating history
- Introducing Alacrity to Federated Cryptography discussed how I plan to ensure that independent third-party clients stay up-to-date or lose the ability to decrypt messages
Recently, NIST published the new Federal Information Protection Standards documents for three post-quantum cryptography algorithms:
- FIPS-203 (ML-KEM, formerly known as CRYSTALS-Kyber),
- FIPS-204 (ML-DSA, formerly known as CRYSTALS-Dilithium)
- FIPS-205 (SLH-DSA, formerly known as SPHINCS+)
The race is now on to implement and begin migrating the Internet to use post-quantum KEMs. (Post-quantum signatures are less urgent.) If you’re curious why, this CloudFlare blog post explains the situation quite well.
Since I’m proposing a new protocol and implementation at the dawn of the era of post-quantum cryptography, I’ve decided to migrate the asymmetric primitives used in my proposals towards post-quantum algorithms where it makes sense to do so.
Art: AJ
The rest of this blog post is going to talk about technical specifics and the decisions I intend to make in both projects, as well as some other topics I’ve been thinking about related to this work.
Which Algorithms, Where?
I’ll discuss these choices in detail, but for the impatient:
- Public Key Directory
- Still just Ed25519 for now
- End-to-End Encryption
- KEMs: X-Wing (Hybrid X25519 and ML-KEM-768)
- Signatures: Still just Ed25519 for now
Virtually all other uses of cryptography is symmetric-key or keyless (i.e., hash functions), so this isn’t a significant change to the design I have in mind.
Post-Quantum Algorithm Selection Criteria
While I am personally skeptical if we will see a practical cryptography-relevant quantum computer in the next 30 years, due to various engineering challenges and a glacial pace of progress on solving them, post-quantum cryptography is still a damn good idea even if a quantum computer doesn’t emerge.Post-Quantum Cryptography comes in two flavors:
- Key Encapsulation Mechanisms (KEMs), which I wrote about previously.
- Digital Signature Algorithms (DSAs).
Originally, my proposals were going to use Elliptic Curve Diffie-Hellman (ECDH) in order to establish a symmetric key over an untrusted channel. Unfortunately, ECDH falls apart in the wake of a crypto-relevant quantum computer. ECDH is the component that will be replaced by post-quantum KEMs.
Additionally, my proposals make heavy use of Edwards Curve Digital Signatures (EdDSA) over the edwards25519 elliptic curve group (thus, Ed25519). This could be replaced with a post-quantum DSA (e.g., ML-DSA) and function just the same, albeit with bandwidth and/or performance trade-offs.
But isn’t post-quantum cryptography somewhat new?
Lattice-based cryptography has been around almost as long as elliptic curve cryptography. One of the first designs, NTRU, was developed in 1996.Meanwhile, ECDSA was published in 1992 by Dr. Scott Vanstone (although it was not made a standard until 1999). Lattice cryptography is pretty well-understood by experts.
However, before the post-quantum cryptography project, there hasn’t been a lot of incentive for attackers to study lattices (unless they wanted to muck with homomorphic encryption).
So, naturally, there is some risk of a cryptanalysis renaissance after the first post-quantum cryptography algorithms are widely deployed to the Internet.
However, this risk is mostly a concern for KEMs, due to the output of a KEM being the key used to encrypt sensitive data. Thus, when selecting KEMs for post-quantum security, I will choose a Hybrid construction.
Hybrid what?
We’re not talking folfs, sonny!Hybrid isn’t just a thing that furries do with their fursonas. It’s also a term that comes up a lot in cryptography.
Unfortunately, it comes up a little too much.
I made this dumb meme with imgflip
When I say we use Hybrid constructions, what I really mean is we use a post-quantum KEM and a classical KEM (such as HPKE‘s DHKEM), then combine them securely using a KDF.Post-quantum KEMs
For the post-quantum KEM, we only really have one choice: ML-KEM. But this choice is actually three choices: ML-KEM-512, ML-KEM-768, or ML-KEM-1024.The security margin on ML-KEM-512 is a little tight, so most cryptographers I’ve talked with recommend ML-KEM-768 instead.
Meanwhile, the NSA wants the US government to use ML-KEM-1024 for everything.
How will you hybridize your post-quantum KEM?
Originally, I was looking to use DHKEM with X25519, as part of the HPKE specification. After switching to post-quantum cryptography, I would need to combine it with ML-KEM-768 in such a way that the whole shebang is secure if either component is secure.But then, why reinvent the wheel here? X-Wing already does that, and has some nice binding properties that a naive combination might not.
So let’s use X-Wing for our KEM.
Notably, OpenMLS is already doing this in their next release.
Art: CMYKat
Post-quantum signatures
So our KEM choice seems pretty straightforward. What about post-quantum signatures?Do we even need post-quantum signatures?
Well, the situation here is not nearly as straightforward as KEMs.
For starters, NIST chose to standardize two post-quantum digital signature algorithms (with a third coming later this year). They are as follows:
- ML-DSA (formerly CRYSTALS-Dilithium), that comes in three flavors:
- ML-DSA-44
- ML-DSA-65
- ML-DSA-87
- SLH-DSA (formerly SPHINCS+), that comes in 24 flavors
- FN-DSA (formerly FALCON), that comes in two flavors but may be excruciating to implement in constant-time (this one isn’t standardized yet)
Since we’re working at the application layer, we’re less worried about a few kilobytes of bandwidth than the networking or X.509 folks are. Relatively speaking, we care about security first, performance second, and message size last.
After all, people ship Electron, React Native, and NextJS apps that load megabytes of JavaScript code to print, “hello world,” and no one bats an eye. A few kilobytes in this context is easily digestible for us.
(As I said, this isn’t true for all layers of the stack. WebPKI in particular feels a lot of pain with large public keys and/or signatures.)
Eliminating post-quantum signature candidates
Performance considerations would eliminate SLH-DSA, which is the most conservative choice. Even with the fastest parameter set (SLH-DSA-128f), this family of algorithms is about 550x slower than Ed25519. (If we prioritize bandwidth, it becomes 8000x slower.)Adopted from CloudFlare’s blog post on post-quantum cryptography.
Between the other two, FN-DSA is a tempting option. Although it’s difficult to implement in constant-time, it offers smaller public key and signature sizes.
However, FN-DSA is not standardized yet, and it’s only known to be safe on specific hardware architectures. (It might be safe on others, but that’s not proven yet.)
In order to allow Fediverse users be secure on a wider range of hardware, this uncertainty would limit our choice of post-quantum signature algorithms to some flavor of ML-DSA–whether stand-alone or in a hybrid construction.
Unlike KEMs, hybrid signature constructions may be problematic in subtle ways that I don’t want to deal with. So if we were to do anything, we would probably choose a pure post-quantum signature algorithm.
Against the Early Adoption of Post-Quantum Signatures
There isn’t an immediate benefit to adopting a post-quantum signature algorithm, as David Adrian explains.The migration to post-quantum cryptography will be a long and difficult road, which is all the more reason to make sure we learn from past efforts, and take advantage of the fact the risk is not imminent. Specifically, we should avoid:
- Standardizing without real-world experimentation
- Standardizing solutions that match how things work currently, but have significant negative externalities (increased bandwidth usage and latency), instead of designing new things to mitigate the externalities
- Deploying algorithms pre-standardization in ways that can’t be easily rolled back
- Adding algorithms that are pre-standardization or have severe shortcomings to compliance frameworks
We are not in the middle of a post-quantum emergency, and nothing points to a surprise “Q-Day” within the next decade. We have time to do this right, and we have time for an iterative feedback loop between implementors, cryptographers, standards bodies, and policymakers.
The situation may change. It may become clear that quantum computers are coming in the next few years. If that happens, the risk calculus changes and we can try to shove post-quantum cryptography into our existing protocols as quickly as possible. Thankfully, that’s not where we are.
David Adrian, Lack of post-quantum security is not plaintext.
Furthermore, there isn’t currently any commitment from the Sigsum developers to adopt a post-quantum signature scheme in the immediate future. They hard-code Ed25519 for the current iteration of the specification.The verdict on digital signature algorithms?
Given all of the above, I’m going to opt to simply not adopt post-quantum signatures until a later date.Version 1 of our design will continue to use Ed25519 despite it not being secure after quantum computers emerge (“Q-Day”).
When the security industry begins to see warning signs of Q-Day being realistically within a decade, we will prioritize migrating to use post-quantum signature algorithms in a new version of our design.
Should something drastic happen that would force us to decide on a post-quantum algorithm today, we would choose ML-DSA-44. However, that’s unlikely for at least several years.
Remember, Store Now, Decrypt Later doesn’t really break signatures the way it would break public-key encryption.
Art: Harubaki
Miscellaneous Technical Matters
Okay, that’s enough about post-quantum for now. I worry that if I keep talking about key encapsulation, some of my regular readers will start a shitty garage band called My KEMical Romance before the end of the year.Let’s talk about some other technical topics related to end-to-end encryption for the Fediverse!
Federated MLS
MLS was implicitly designed with the idea of having one central service for passing messages around. This makes sense if you’re building a product like Signal, WhatsApp, or Facebook Messenger.It’s not so great for federated environments where your Delivery Service may be, in fact, more than one service (i.e., the Fediverse). An expired Internet Draft for Federated MLS talks about these challenges.
If we wanted to build atop MLS for group key agreement (like has been suggested before), we’d need to tackle this in a way that doesn’t cede control of MLS epochs to any server that gets compromised.
How to Make MLS Tolerate Federation
First, the Authentication Service component can be replaced by client-side protocols, where public keys are sourced from the Public Key Directory (PKD) services.That is to say, from the PKD, you can fetch a valid list of Ed25519 public keys for each participant in the group.
When a group is created, the creator’s Ed25519 public key is known. Everyone they invite, their software necessarily has to know their Ed25519 public key in order to invite them.
In order for a group action to be performed, it must be signed by one of the public keys enrolled into the group list. Additionally, some actions may be limited by permissions attached at the time of the invite (or elevated by a more privileged user; which necessitates another group action).
By requiring a valid signature from an existing group member, we remove the capability of the Fediverse instance that’s hosting the discussion group to meddle with it in any way (unless, for some reason, the server is somehow also a participant that was invited).
But therein lies the other change we need to make: In many cases, groups will span multiple Fediverse servers, so groups shouldn’t be dependent on a single instance.
Spreading The Load Across Instances
Put simply, we need a consensus algorithm to determine which instance hosts messages. We could look to Raft as a starting point, but whatever we land on should be fair, fault-tolerant, and deterministic to all participants who can agree on the same symmetric keying material at some point in time.To that end, I propose using an additional HKDF output from the Group Key Agreement protocol to select a “leader” for all instances involved in the group, weighted by the number of participants on each instance.
Then, every N messages (where N >= 1), a new leader is elected by the same deterministic protocol. This will be performed entirely client-side, and clients will choose N. I will refer to this as a sub-epoch, since it doesn’t coincide with a new MLS epoch.
Since the agreed-upon group key always ratchets forward when a group action occurs (i.e., whenever there’s a new epoch), getting another KDF output to elect the next leader is straightforward.
This isn’t a fully fleshed out idea. Building consensus protocols that can handle real-world operational issues is heavily specialized work and there’s a high risk of falling to the illusion of safety until it’s too late. I will probably need help with this component.
That said, we aren’t building an anonymity network, so the cost of getting a detail wrong isn’t measurable in blood.
We aren’t really concerned with Sybil attacks. Winning the election just means you’re responsible for being a dumb pipe for ciphertext. Client software should trust the instance software as little as possible.
We also probably don’t need to worry about availability too much. Since we’re building atop ActivityPub, when a server goes down, the other instances can hold encrypted messages in the outbox for the host instance to pick up when it’s back online.
If that’s not satisfactory, we could also select both a primary and secondary leader for each epoch (and sub-epoch), to have built-in fail-over when more than one instance is involved in a group conversation.
If messages aren’t being delivered for an unacceptable period of time, client software can forcefully initiate a new leader election by expiring the current MLS epoch (i.e. by rotating their own public key and sending the relevant bundle to all other participants).
Art: Kyume
Those are just some thoughts. I plan to talk it over with people who have more expertise in the relevant systems.
And, as with the rest of this project, I will write a formal specification for this feature before I write a single line of production code.
Abuse Reporting
I could’ve swore I talked about this already, but I can’t find it in any of my previous ramblings, so here’s a good place as any.The intent for end-to-end encryption is privacy, not secrecy.
What does this mean exactly? From the opening of Eric Hughes’ A Cypherpunk’s Manifesto:
Privacy is necessary for an open society in the electronic age. Privacy is not secrecy.A private matter is something one doesn’t want the whole world to know, but a secret matter is something one doesn’t want anybody to know.
Privacy is the power to selectively reveal oneself to the world.
Eric Hughes (with whitespace and emphasis added)
Unrelated: This is one reason why I use “secret key” when discussing asymmetric cryptography, rather than “private key”. It also lends towardssk
andpk
as abbreviations, whereas “private” and “public” both start with the letter P, which is annoying.With this distinction in mind, abuse reporting is not inherently incompatible with end-to-end encryption or any other privacy technology.
In fact, it’s impossible to create useful social technology without the ability for people to mitigate abuse.
So, content warning: This is going to necessarily discuss some gross topics, albeit not in any significant detail. If you’d rather not read about them at all, feel free to skip this section.
Art: CMYKat
When thinking about the sorts of problems that call for an abuse reporting mechanism, you really need to consider the most extreme cases, such as someone joining group chats to spam unsuspecting users with unsolicited child sexual abuse material (CSAM), flashing imagery designed to trigger seizures, or graphic depictions of violence.
That’s gross and unfortunate, but the reality of the Internet.
However, end-to-end encryption also needs to prioritize privacy over appeasing lazy cops who would rather everyone’s devices include a mandatory little cop that watches all your conversations and snitches on you if you do anything that might be illegal, or against the interest of your government and/or corporate masters. You know the type of cop. They find privacy and encryption to be rather inconvenient. After all, why bother doing their jobs (i.e., actual detective work) when you can just criminalize end-to-end encryption and use dragnet surveillance instead?
Whatever we do, we will need to strike a balance that protects users’ privacy, without any backdoors or privileged access for lazy cops, with community safety.
Thus, the following mechanisms must be in place:
- Groups must have the concept of an “admin” role, who can delete messages on behalf of all users and remove users from the group. (Signal currently doesn’t have this.)
- Users must be able to delete messages on their own device and block users that send abusive content. (The Fediverse already has this sort of mechanism, so we don’t need to be inventive here.)
- Users should have the ability to report individual messages to the instance moderators.
I’m going to focus on item 3, because that’s where the technically and legally thorny issues arise.
Keep in mind, this is just a core-dump of thoughts about this topic, and I’m not committing to anything right now.
Technical Issues With Abuse Reporting
First, the end-to-end encryption must be immune to Invisible Salamanders attacks. If it’s not, go back to the drawing board.Every instance will need to have a moderator account, who can receive abuse reports from users. This can be a shared account for moderators or a list of moderators maintained by the server.
When an abuse report is sent to the moderation team, what needs to happen is that the encryption keys for those specific messages are re-wrapped and sent to the moderators.
So long as you’re using a forward-secure ratcheting protocol, this doesn’t imply access to the encryption keys for other messages, so the information disclosed is limited to the messages that a participant in the group consents to disclosing. This preserves privacy for the rest of the group chat.
When receiving a message, moderators should not only be able to see the reported message’s contents (in the order that they were sent), but also how many messages were omitted in the transcript, to prevent a type of attack I colloquially refer to as “trolling through omission”. This old meme illustrates the concept nicely:
Trolling through omission.
And this all seems pretty straightforward, right? Let users protect themselves and report abuse in such a way that doesn’t invalidate the privacy of unrelated messages or give unfettered access to the group chats. “Did Captain Obvious write this section?”But things aren’t so clean when you consider the legal ramifications.
Potential Legal Issues With Abuse Reporting
Suppose Alice, Bob, and Troy start an encrypted group conversation. Alice is the group admin and delete messages or boot people from the chat.One day, Troy decides to send illegal imagery (e.g., CSAM) to the group chat.
Bob immediately, disgusted, reports it to his instance moderator (Dave) as well as Troy’s instance moderator (Evelyn). Alice then deletes the messages for her and Bob and kicks Troy from the chat.
Here’s where the legal questions come in.
If Dave and Evelyn are able to confirm that Troy did send CSAM to Alice and Bob, did Bob’s act of reporting the material to them count as an act of distribution (i.e., to Dave and/or Evelyn, who would not be able to decrypt the media otherwise)?
If they aren’t able to confirm the reports, does Alice’s erasure count as destruction of evidence (i.e., because they cannot be forwarded to law enforcement)?
Are Bob and Alice legally culpable for possession? What about Dave and Evelyn, whose servers are hosting the (albeit encrypted) material?
It’s not abundantly clear how the law will intersect with technology here, nor what specific technical mechanisms would need to be in place to protect Alice, Bob, Dave, and Evelyn from a particularly malicious user like Troy.
Obviously, I am not a lawyer. I have an understanding with my lawyer friends that I will not try to interpret law or write my own contracts if they don’t roll their own crypto.
That said, I do have some vague ideas for mitigating the risk.
Ideas For Risk Mitigation
To contend with this issue, one thing we could do is separate the abuse reporting feature from the “fetch and decrypt the attached media” feature, so that while instance moderators will be capable of fetching the reported abuse material, it doesn’t happen automatically.When the “reason” attached to an abuse report signals CSAM in any capacity, the client software used by moderators could also wholesale block the download of said media.
Whether that would be sufficient mitigate the legal matters raised previously, I can’t say.
And there’s still a lot of other legal uncertainty to figure out here.
- Do instance moderators actually have a duty to forward CSAM reports to law enforcement?
- If so, how should abuse forwarding to be implemented?
- How do we train law enforcement personnel to receive and investigate these reports WITHOUT frivolously arresting the wrong people or seizing innocent Fediverse servers?
- How do we ensure instance admins are broadly trained to handle this?
- How do we deal with international law?
- How do we prevent scope creep?
- While there is public interest in minimizing the spread of CSAM, which is basically legally radioactive, I’m not interested in ever building a “snitch on women seeking reproductive health care in a state where abortion is illegal” capability.
- Does Section 230 matter for any of these questions?
We may not know the answers to these questions until the courts make specific decisions that establish relevant case law, or our governments pass legislation that clarifies everyone’s rights and responsibilities for such cases.
Until then, the best answer may simply to do nothing.
That is to say, let admins delete messages for the whole group, let users delete messages they don’t want on their own hardware, and let admins receive abuse reports from their users… but don’t do anything further.
Okay, we should definitely require an explicit separate action to download and decrypt the media attached to a reported message, rather than have it be automatic, but that’s it.
What’s Next?
For the immediate future, I plan on continuing to develop the Federated Public Key Directory component until I’m happy with its design. Then, I will begin developing the reference implementations for both client and server software.Once that’s in a good state, I will move onto finishing the E2EE specification. Then, I will begin building the client software and relevant server patches for Mastodon, and spinning up a testing instance for folks to play with.
Timeline-wise, I would expect most of this to happen in 2025.
I wish I could promise something sooner, but I’m not fond of moving fast and breaking things, and I do have a full time job unrelated to this project.
Hopefully, by the next time I pen an update for this project, we’ll be closer to launching. (And maybe I’ll have answers to some of the legal concerns surrounding abuse reporting, if we’re lucky.)
https://soatok.blog/2024/09/13/e2ee-for-the-fediverse-update-were-going-post-quantum/
#E2EE #endToEndEncryption #fediverse #FIPS #Mastodon #postQuantumCryptography
I don’t consider myself exceptional in any regard, but I stumbled upon a few cryptography vulnerabilities in Matrix’s Olm library with so little effort that it was nearly accidental.
It should not be this easy to find these kind of issues in any product people purportedly rely on for private messaging, which many people evangelize incorrectly as a Signal alternative.
Later, I thought I identified an additional vulnerability that would have been much worse, but I was wrong about that one. For the sake of transparency and humility, I’ll also describe that in detail.
This post is organized as follows:
- Disclosure Timeline
- Vulnerabilities in Olm (Technical Details)
- Recommendations
- Background Information
- An Interesting Non-Issue That Looked Critical
I’ve opted to front-load the timeline and vulnerability details to respect the time of busy security professionals.
Please keep in mind that this website is a furry blog, first and foremost, that sometimes happens to cover security and cryptography topics.Many people have, over the years, assumed the opposite and commented accordingly. The ensuing message board threads are usually is a waste of time and energy for everyone involved. So please adjust your expectations.
Art by Harubaki
If you’re curious, you can learn more here.
Disclosure Timeline
- 2024-05-15: I took a quick look at the Matrix source code. I identified two issues and emailed them to their
security@
email address.
In my email, I specify that I plan to disclose my findings publicly in 90 days (i.e. on August 14), in adherence with industry best practices for coordinated disclosure, unless they request an extension in writing. - 2024-05-16: I checked something else on a whim and find a third issue, which I also email to their
security@
email address. - 2024-05-17: Matrix security team confirms receipt of my reports.
- 2024-05-17: I follow up with a suspected fourth finding–the most critical of them all. They point out that it is not actually an issue, because I overlooked an important detail in how the code is architected. Mea culpa!
- 2024-05-18: A friend discloses a separate finding with Matrix: Media can be decrypted to multiple valid plaintexts using different keys and Malicious homeservers can trick Element/Schildichat into revealing links in E2EE rooms.
They instructed the Matrix developers to consult with me if they needed cryptography guidance. I never heard from them on this externally reported issue. - 2024-07-12: I shared this blog post draft with the Matrix security team while reminding them of the public disclosure date.
- 2024-07-31: Matrix pushes a commit that announces that libolm is deprecated.
- 2024-07-31: I email the Matrix security team asking if they plan to fix the reported issues (and if not, if there’s any other reason I should withhold publication).
- 2024-07-31: Matrix confirms they will not fix these issues (due to its now deprecated status), but ask that I withhold publication until the 14th as originally discussed.
- 2024-08-14: This blog post is publicly disclosed to the Internet.
- 2024-08-14: The lead Matrix dev claims they already knew about these issues, and, in fact, knowingly shipped cryptography code that was vulnerable to side-channel attacks for years. See Addendum.
- 2024-08-23: MITRE has assigned CVE IDs to these three findings.
Vulnerabilities in Olm
I identified the following issues with Olm through a quick skim of their source code on Gitlab:
- AES implementation is vulnerable to cache-timing attacks
- Ed25519 signatures are malleable
- Timing leakage in base64 decoding of private key material
This is sorted by the order in which they were discovered, rather than severity.
AES implementation is vulnerable to cache-timing attacks
a.k.a. CVE-2024-45191
Olm ships a pure-software implementation of AES, rather than leveraging hardware acceleration.
// Substitutes a word using the AES S-Box.WORD SubWord(WORD word){unsigned int result;result = (int)aes_sbox[(word >> 4) & 0x0000000F][word & 0x0000000F];result += (int)aes_sbox[(word >> 12) & 0x0000000F][(word >> 8) & 0x0000000F] << 8;result += (int)aes_sbox[(word >> 20) & 0x0000000F][(word >> 16) & 0x0000000F] << 16;result += (int)aes_sbox[(word >> 28) & 0x0000000F][(word >> 24) & 0x0000000F] << 24;return(result);}
The code in question is called from this code, which is in turn used to actually encrypt messages.
Software implementations of AES that use a look-up table for the SubWord step of the algorithm are famously susceptible to cache-timing attacks.
This kind of vulnerability in software AES was previously used to extract a secret key from OpenSSL and dm-crypt in about 65 milliseconds. Both papers were published in 2005.
A general rule in cryptography is, “attacks only get better; they never get worse“.
As of 2009, you could remotely detect a timing difference of about 15 microseconds over the Internet with under 50,000 samples. Side-channel exploits are generally statistical in nature, so such a sample size is generally not a significant mitigation.
How is this code actually vulnerable?
In the above code snippet, the vulnerability occurs inaes_sbox[/* ... */][/* ... */]
.
Due to the details of how the AES block cipher works, the input variable (word
) is a sensitive value.
Software written this way allows attackers to detect whether or not a specific value was present in one of the processor’s caches.
To state the obvious: Cache hits are faster than cache misses. This creates an observable timing difference.
Such a timing leak allows the attacker to learn the value that was actually stored in said cache. You can directly learn this from other processes on the same hardware, but it’s also observable over the Internet (with some jitter) through the normal operation of vulnerable software.
See also: cryptocoding’s description for table look-ups indexed by secret data.
How to mitigate this cryptographic side-channel
The correct way to solve this problem is to use hardware accelerated AES, which uses distinct processor features to implement the AES round function and side-steps any cache-timing shenanigans with the S-box.
Not only is this more secure, but it’s faster and uses less energy too!
If you’re also targeting devices that don’t have hardware acceleration available, you should first use hardware acceleration where possible, but then fallback to a bitsliced implementation such as the one in Thomas Pornin’s BearSSL.
See also: the BearSSL documentation for constant-time AES.
Art by AJ
Ed25519 signatures are malleable
a.k.a. CVE-2024-45193
Ed25519 libraries come in various levels of quality regarding signature validation criteria; much to the chagrin of cryptography engineers everywhere. One of those validation criteria involves signature malleability.
Signature malleability usually isn’t a big deal for most protocols, until suddenly you discover a use case where it is. If it matters, that usually that means you’re doing something with cryptocurrency.
Briefly, if your signatures are malleable, that means you can take an existing valid signature for a given message and public key, and generate a second valid signature for the same message. The utility of this flexibility is limited, and the impact depends a lot on how you’re using signatures and what properties you hope to get out of them.
For ECDSA, this means that for a given signature , a second signature is also possible (where is the order of the elliptic curve group you’re working with).
Matrix uses Ed25519, whose malleability is demonstrated between and .
This is trivially possible because S is implicitly reduced modulo the order of the curve, , which is a 253-bit number (0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed
) and S is encoded as a 256-bit number.
The Ed25519 library used within Olm does not ensure that , thus signatures are malleable. You can verify this yourself by looking at the Ed25519 verification code.
int ed25519_verify(const unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key) { unsigned char h[64]; unsigned char checker[32]; sha512_context hash; ge_p3 A; ge_p2 R; if (signature[63] & 224) { return 0; } if (ge_frombytes_negate_vartime(&A, public_key) != 0) { return 0; } sha512_init(&hash); sha512_update(&hash, signature, 32); sha512_update(&hash, public_key, 32); sha512_update(&hash, message, message_len); sha512_final(&hash, h); sc_reduce(h); ge_double_scalarmult_vartime(&R, h, &A, signature + 32); ge_tobytes(checker, &R); if (!consttime_equal(checker, signature)) { return 0; } return 1;}
This is almost certainly a no-impact finding (or low-impact at worst), but still an annoying one to see in 2024.
If you’d like to learn more, this page is a fun demo of Ed25519 malleability.
To mitigate this, I recommend implementing these checks from libsodium.
Art: CMYKat
Timing leakage in base64 decoding of private key material
a.k.a. CVE-2024-45192
If you weren’t already tired of cache-timing attacks based on table look-ups from AES, the Matrix base64 code is also susceptible to the same implementation flaw.
while (pos != end) { unsigned value = DECODE_BASE64[pos[0] & 0x7F]; value <<= 6; value |= DECODE_BASE64[pos[1] & 0x7F]; value <<= 6; value |= DECODE_BASE64[pos[2] & 0x7F]; value <<= 6; value |= DECODE_BASE64[pos[3] & 0x7F]; pos += 4; output[2] = value; value >>= 8; output[1] = value; value >>= 8; output[0] = value; output += 3;}
The base64 decoding function in question is used to load the group session key, which means the attack published in this paper almost certainly applies.
How would you mitigate this leakage?
Steve Thomas (one of the judges of the Password Hashing Competition, among other noteworthy contributions) wrote some open source code a while back that implements base64 encoding routines in constant-time.
The real interesting part is how it avoids a table look-up by using arithmetic (from this file):
// Base64 character set:// [A-Z] [a-z] [0-9] + /// 0x41-0x5a, 0x61-0x7a, 0x30-0x39, 0x2b, 0x2finline int base64Decode6Bits(char src){int ch = (unsigned char) src;int ret = -1;// if (ch > 0x40 && ch < 0x5b) ret += ch - 0x41 + 1; // -64ret += (((0x40 - ch) & (ch - 0x5b)) >> 8) & (ch - 64);// if (ch > 0x60 && ch < 0x7b) ret += ch - 0x61 + 26 + 1; // -70ret += (((0x60 - ch) & (ch - 0x7b)) >> 8) & (ch - 70);// if (ch > 0x2f && ch < 0x3a) ret += ch - 0x30 + 52 + 1; // 5ret += (((0x2f - ch) & (ch - 0x3a)) >> 8) & (ch + 5);// if (ch == 0x2b) ret += 62 + 1;ret += (((0x2a - ch) & (ch - 0x2c)) >> 8) & 63;// if (ch == 0x2f) ret += 63 + 1;ret += (((0x2e - ch) & (ch - 0x30)) >> 8) & 64;return ret;}
Any C library that handles base64 codecs for private key material should use a similar implementation. It’s fine to have a faster base64 implementation for non-secret data.
Worth noting: Libsodium also provides a reasonable Base64 codec.
Recommendations
These issues are not fixed in libolm.
Instead of fixing libolm, the Matrix team recommends all Matrix clients adopt vodozemac.
I can’t speak to the security of vodozemac.
Art: CMYKat
But I can speak against the security of libolm, so moving to vodozemac is probably a good idea. It was audited by Least Authority at one point, so it’s probably fine.
Most Matrix clients that still depended on libolm should treat this blog as public 0day, unless the Matrix security team already notified you about these issues.
Background Information
If you’re curious about the backstory and context of these findings, read on.Otherwise, feel free to skip this section. It’s not pertinent to most audiences. The people that need to read it already know who they are.
End-to-end encryption is one of the topics within cryptography that I find myself often writing about.
In 2020, I wrote a blog post covering end-to-end encryption for application developers. This was published several months after another blog I wrote covering gripes with AES-GCM, which included a shallow analysis of how Signal uses the algorithm for local storage.
In 2021, I published weaknesses in another so-called private messaging app called Threema.
In 2022, after Elon Musk took over Twitter, I joined the Fediverse and sought to build end-to-end encryption support for direct messages into ActivityPub, starting with a specification. Work on this effort was stalled while trying to solve Public Key distribution in a federated environment (which I hope to pick up soon, but I digress).
Earlier this year, the Telegram CEO started fearmongering about Signal with assistance from Elon Musk, so I wrote a blog post urging the furry fandom to move away from Telegram and start using Signal more. As I had demonstrated years prior, I was familiar with Signal’s code and felt it was a good recommendation for security purposes (even if its user experience needs significant work).
I thought that would be a nice, self-contained blog post. Some might listen, most would ignore it, but I could move on with my life.
I was mistaken about that last point.
Art by AJ
An overwhelming number of people took it upon themselves to recommend or inquire about Matrix, which prompted me to hastily scribble down my opinion on Matrix so that I might copy/paste a link around and save myself a lot of headache.
Just when I thought the firehose was manageable and I could move onto other topics, one of the Matrix developers responded to my opinion post.
Thus, I decided to briefly look at their source code and see if any major or obvious cryptography issues would fall out of a shallow visual scan.
Since you’re reading this post, you already know how that ended.
Credit: CMYKat
Since the first draft of this blog post was penned, I also outlined what I mean when I say an encrypted messaging app is a Signal competitor or not, and published my opinion on XMPP+OMEMO (which people also recommend for private messaging).
Why mention all this?
Because it’s important to know that I have not audited the Olm or Megolm codebases, nor even glanced at their new Rust codebase.
The fact is, I never intended to study Matrix. I was annoyed into looking at it in the first place.
My opinion of their project was already calcified by the previously discovered practically-exploitable cryptographic vulnerabilities in Matrix in 2022.
The bugs described above are the sort of thing I mentally scan for when I first look at a project just to get a feel for the maturity of the codebase. I do this with the expectation (hope, really) of not finding anything at all.
(If you want two specific projects that I’ve subjected to a similar treatment, and failed to discover anything interesting in: Signal and WireGuard. These two set the bar for cryptographic designs.)
It’s absolutely bonkers that an AES cache timing vulnerability was present in their code in 2024.
It’s even worse when you remember that I was inundated with Matrix evangelism in response to recommending furries use Signal.I’m a little outraged because of how irresponsible this is, in context.
It’s so bad that I didn’t even need to clone their git repository, let alone run basic static analysis tools locally.
So if you take nothing else away from this blog post, let it be this:
There is roughly a 0% chance that I got extremely lucky in my mental grep
and found the only cryptography implementation flaws in their source code. I barely tried at all and found these issues.
I would bet money on there being more bugs or design flaws that I didn’t find, because this discovery was the result of an extremely half-assed effort to blow off steam.
Wasn’t libolm deprecated in May 2022?
The Matrix developers like to insist that their new Rust hotness “vodozemac” is what people should be using today.
I haven’t looked at vodozemac at all, but let’s pretend, for the sake of argument, that its cryptography is actually secure.
(This is very likely if they turn out to be using RustCrypto for their primitives, but I don’t have the time or energy for that nerd snipe, so I’m not going to look. Least Authority did audit their Rust library, for what it’s worth, and Least Authority isn’t clownshoes.)
It’s been more than 2 years since they released vodozemac. What does the ecosystem penetration for this new library look like, in practice?
A quick survey of the various Matrix clients on GitHub says that libolm is still the most widely used cryptography implementation in the Matrix ecosystem (as of this writing):
Matrix Client | Cryptography Backend |
---|---|
https://github.com/tulir/gomuks | libolm (1, 2) |
https://github.com/niochat/nio | libolm (1, 2) |
https://github.com/ulyssa/iamb | vodozemac (1, 2) |
https://github.com/mirukana/mirage | libolm (1) |
https://github.com/Pony-House/Client | libolm (1) |
https://github.com/MTRNord/cetirizine | vodozemac (1) |
https://github.com/nadams/go-matrixcli | none |
https://github.com/mustang-im/mustang | libolm (1) |
https://github.com/marekvospel/libretrix | libolm (1) |
https://github.com/yusdacra/icy_matrix | none |
https://github.com/ierho/element | libolm (through the python SDK) |
https://github.com/mtorials/cordless | none |
https://github.com/hwipl/nuqql-matrixd | libolm (through the python SDK) |
https://github.com/maxkratz/element-web | vodozemac (1, 2, 3, 4) |
https://github.com/asozialesnetzwerk/riot | libolm (wasm file) |
https://github.com/NotAlexNoyle/Versi | libolm (1, 2) |
3 of the 16 clients surveyed use the new vodozemac library. 10 still use libolm, and 3 don’t appear to implement end-to-end encryption at all.
If we only focus on clients that support E2EE, vodozemac has successfully been adopted by 19% of the open source Matrix clients on GitHub.
I deliberately excluded any repositories that were archived or clearly marked as “old” or “legacy” software, because including those would artificially inflate the representation of libolm. It would make for a more compelling narrative to do so, but I’m not trying to be persuasive here.
Deprecation policies are a beautiful lie. The impact of a vulnerability in Olm or Megolm is still far-reaching, and should be taken seriously by the Matrix community.
Worth calling out: this quick survey, which is based on a GitHub Topic, certainly misses other implementations. Both FluffyChat and Cinny, which were not tagged with this GitHub Topic, depend a language-specific Olm binding.These bindings in turn wrap libolm rather than the Rust replacement, vodozemac.
But the official clients…
I thought the whole point of choosing Matrix over something like Signal is to be federated, and run your own third-party clients?
If we’re going to insist that everyone should be using Element if they want to be secure, that defeats the entire marketing point about third-party clients that Matrix evangelists cite when they decry Signal’s centralization.
So I really don’t want to hear it.
An Interesting Non-Issue That Looked Critical
As I mentioned in the timeline at the top, I thought I found a fourth issue with Matrix’s codebase. Had I been correct, this would have been a critical severity finding that the entire Matrix ecosystem would need to melt down to remediate.
Fortunately for everyone, I made a mistake, and there is no fourth vulnerability after all.
However, I thought it would be interesting to write about what I thought I found, the impact it would have had if it were real, and why I believed it to be an issue.
Let’s start with the code in question:
void ed25519_sign(unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key, const unsigned char *private_key) { sha512_context hash; unsigned char hram[64]; unsigned char r[64]; ge_p3 R; sha512_init(&hash); sha512_update(&hash, private_key + 32, 32); sha512_update(&hash, message, message_len); sha512_final(&hash, r); sc_reduce(r); ge_scalarmult_base(&R, r); ge_p3_tobytes(signature, &R); sha512_init(&hash); sha512_update(&hash, signature, 32); sha512_update(&hash, public_key, 32); sha512_update(&hash, message, message_len); sha512_final(&hash, hram); sc_reduce(hram); sc_muladd(signature + 32, hram, private_key, r);}
The highlighted segment is doing pointer arithmetic. This means it’s reading 32 bytes, starting from the 32nd byte in private_key
.
What’s actually happening here is: private_key
is the SHA512 hash of a 256-bit seed. If you look at the function prototype, you’ll notice that public_key
is a separate input.
Virtually every other Ed25519 implementation I’ve ever looked at before expected users to provide a 32 byte seed followed by the public key as a single input.
This led me to believe that this private_key + 32
pointer arithmetic was actually using the public key for calculating r
.
The variable r
(not to be confused with big R) generated via the first SHA512 is the nonce for a given signature, it must remain secret for Ed25519 to remain secure.
If r
is known to an attacker, you can do some arithmetic to recover the secret key from a single signature.
Because I had mistakenly believed that r
was calculated from the SHA512 of only public inputs (the public key and message), which I must emphasize isn’t correct, I had falsely concluded that any previously intercepted signature could be used to steal user’s private keys.
Credit: CMYKat
But because private_key
was actually the full SHA512 hash of the seed, rather than the seed concatenated with the public key, this pointer arithmetic did NOT use the public key for the calculation of r
, so this vulnerability does not exist.
If the code did what I thought it did, however, this would have been a complete fucking disaster for the Matrix ecosystem. Any previously intercepted message would have allowed an attacker to recover a user’s secret key and impersonate them. It wouldn’t be enough to fix the code; every key in the ecosystem would need to be revoked and rotated.
Whew!
I’m happy to be wrong about this one, because that outcome is a headache nobody wants.
So no action is needed, right?
Well, maybe.
Matrix’s library was not vulnerable, but I honestly wouldn’t put it past software developers at large to somehow, somewhere, use the public key (rather than a secret value) to calculate the EdDSA signature nonces as described in the previous section.
To that end, I would like to propose a test vector be added to the Wycheproof test suite to catch any EdDSA implementation that misuses the public key in this way.
Then, if someone else screws up their Ed25519 implementation in the exact way I thought Matrix was, the Wycheproof tests will catch it.
For example, here’s a vulnerable test input for Ed25519:
{ "should-fail": true, "secret-key": "d1d0ef849f9ec88b4713878442aeebca5c7a43e18883265f7f864a8eaaa56c1ef3dbb3b71132206b81f0f3782c8df417524463d2daa8a7c458775c9af725b3fd", "public-key": "f3dbb3b71132206b81f0f3782c8df417524463d2daa8a7c458775c9af725b3fd", "message": "Test message", "signature": "ffc39da0ce356efb49eb0c08ed0d48a1cadddf17e34f921a8d2732a33b980f4ae32d6f5937a5ed25e03a998e4c4f5910c931b31416e143965e6ce85b0ea93c09"}
A similar test vector would also be worth creating for Ed448, but the only real users of Ed448 were the authors of the xz backdoor, so I didn’t bother with that.
(None of the Project Wycheproof maintainers knew this suggestion is coming, by the way, because I was respecting the terms of the coordinated disclosure.)
Closing Thoughts
Despite finding cryptography implementation flaws in Matric’s Olm library, my personal opinion on Matrix remains largely unchanged from 2022. I had already assumed it would not meet my bar for security.
Cryptography engineering is difficult because the vulnerabilities you’re usually dealing with are extremely subtle. (Here’s an unrelated example if you’re not convinced of this general observation.) As SwiftOnSecurity once wrote:
https://twitter.com/SwiftOnSecurity/status/832058185049579524
The people that developed Olm and Megolm has not proven themselves ready to build a Signal competitor. In balance, most teams are not qualified to do so.
I really wish the Matrix evangelists would accept this and stop trying to cram Matrix down other people’s throats when they’re talking about problems with other platforms entirely.
More important for the communities of messaging apps:You don’t need to be a Signal competitor. Having E2EE is a good thing on its own merits, and really should be table stakes for any social application in 2024.
It’s only when people try to advertise their apps as a Signal alternative (or try to recommend it instead of Signal), and offer less security, that I take offense.
Just be your own thing.
My work-in-progress proposal to bring end-to-end encryption to the Fediverse doesn’t aim to compete with Signal. It’s just meant to improve privacy, which is a good thing to do on its own merits.
If I never hear Matrix evangelism again after today, it would be far too soon.
If anyone feels like I’m picking on Matrix, don’t worry: I have far worse things to say about Telegram, Threema, XMPP+OMEMO, Tox, and a myriad other projects that are hungry for Signal’s market share but don’t measure up from a cryptographic security perspective.
If Signal fucked up as bad as these projects, my criticism of Signal would be equally harsh. (And remember, I have looked at Signal before.)
Addendum (2024-08-14)
One of the lead Matrix devs posted a comment on Hacker News after this blog post went live that I will duplicate here:
the author literally picked random projects from github tagged as matrix, without considering their prevalence or whether they are actually maintained etc.if you actually look at % of impacted clients, it’s tiny.
meanwhile, it is very unclear that any sidechannel attack on a libolm based client is practical over the network (which is why we didn’t fix this years ago). After all, the limited primitives are commented on in the readme and https://github.com/matrix-org/olm/issues/3 since day 1.
So the Matrix developers already knew about these vulnerabilities, but deliberately didn’t fix them, for years.
Congratulations, you’ve changed my stance. It used to be “I don’t consider Matrix a Signal alternative and they’ve had some embarrassing and impactful crypto bugs but otherwise I don’t care”. Now it’s a stronger stance:
Don’t use Matrix.
I had incorrectly assumed ignorance, when it was in fact negligence.
There’s no reasonable world in which anyone should trust the developers of cryptographic software (i.e., libolm) that deliberately ships with side-channels for years, knowing they’re present, and never bother to fix them.
This is fucking clownshoes.
https://soatok.blog/2024/08/14/security-issues-in-matrixs-olm-library/
#crypto #cryptography #endToEndEncryption #Matrix #sideChannels #vuln
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
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
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
I have been a begrudging user of Telegram for years simply because that’s what all the other furries use, despite their cryptography being legendarily bad.When I signed up, I held my nose and expressed my discontent at Telegram by selecting a username that’s a dig at MTProto’s inherent insecurity against chosen ciphertext attacks:
IND_CCA3_Insecure
.Art: CMYKat
I wrote about Furries and Telegram before, and included some basic privacy recommendations. As I said there: Telegram is not a private messenger. You shouldn’t think of it as one.
Recent Developments
Telegram and Elon Muck have recently begun attacking Signal and trying to paint it as insecure.Matthew Green has a Twitter thread (lol) about it, but you can also read a copy here (archive 1, archive 2, PDF).
https://twitter.com/matthew_d_green/status/1789688236933062767
https://twitter.com/matthew_d_green/status/1789689315624169716
https://twitter.com/matthew_d_green/status/1789690652399170013
https://twitter.com/matthew_d_green/status/1789691417721282958
Et cetera.
This is shitty, and exacerbates a growing problem on Telegram: The prevalence of crypto-bros and fascist groups using it to organize.
Why Signal is Better for Furries
First, Signal has sticker packs now. If you want to use mine, here you go.For years, the main draw for furries to Telegram over Signal was sticker packs. This is a solved problem.
Second, you can setup a username and keep your phone number private. You don’t need to give your phone number to strangers anymore!
(This used to be everyone’s criticism of Signal, but the introduction of usernames made it moot.)
Finally, it’s trivial for Americans to setup a second Signal account using Twilio or Google Voice, so you can compartmentalize your furry posting from the phone number your coworkers or family is likely to know.
(Note: I cannot speak to how to deal with technology outside of America, because I have never lived outside America for any significant length of time and do not know your laws. If this is relevant to you, ask someone in your country to help figure out how to navigate technological and political issues pertinent to your country; I am not local to you and have no fucking clue.)
The last two considerations were really what stopped furries (or queer people in general, really) from using Signal.
Why Signal?
There are two broadly-known private messaging apps that use state-of-the-art cryptography to ensure your messages are private, and one of them is owned by Meta (a.k.a., Facebook, which owns WhatsApp). So Signal is the only real option in my book.That being said, Cwtch certainly looks like it may be promising in the near future. However, I have not studied its cryptography in depth yet. Neither has it been independently audited to my knowledge.
It’s worth pointing out that the lead developer of Cwtch is wrote a book titled Queer Privacy, so she’s overwhelmingly more likely to be receptive to the threat models faced by the furry community (which is overwhelmingly LGBTQ+).
For the sake of expedience, today, Signal is a “yes” and Cwtch is a hopeful “maybe”.
How I Setup a Second Signal Account
I own a Samsung S23, which means I can’t just use the vanilla Android tutorials for setting up a second profile on my device. Instead, I had to use the “Secure Folder” feature. The Freedom of the Press Foundation has more guidance worth considering.If you don’t own a Samsung phone, you don’t need to bother with this “Secure Folder” feature (as the links above will tell you). You can just set up a work profile and get the same result! You probably also can’t access the same feature, since that’s a Samsung exclusive idiom. Don’t sweat it.
I don’t know anything about Apple products, so I can’t help you there, but there’s probably a way to set it up for yourself too. (If not, maybe consider this a good reason to stop giving abusive corporations like Apple money?)
The other piece of the puzzle you need is a second phone number. Google Voice is one way to acquire one; the other is to setup a Twilio account. There are plenty of guides online for doing that.
(Luckily, I’ve had one of these for several years, so I just used that.)
Why does Signal require a phone number?
The historical reason is that Signal was a replacement for text messaging (a.k.a., SMS). That’s probably still the official reason (though they don’t support SMS anymore).From what I understand, the Signal development team has always been much more concerned about privacy for people that own mobile phones, but not computers, than they were concerned about the privacy of people that own computers, but not mobile phones.
After all, if you pick a random less privileged person, especially homeless or from a poor country, they’re overwhelmingly more likely to have a mobile phone than a computer. This doesn’t scratch the itch of people who would prefer to use PGP, but it does prioritize the least privileged people’s use case.
Their workflow, therefore, optimized for people that own a phone number. And so, needing a phone number to sign up wasn’t ever a problem they worried about for the people they were most interested in protecting.
Fortunately, using Signal doesn’t immediately reveal your phone number to anyone you want to chat with, ever since they introduced usernames. You still need one to register.
Tell Your Friends
I understand that the network effect is real. But it’s high time furries jettisoned Telegram as a community.Lazy edit of the “Friendship Ended” meme
Finally, Signal is developed and operated by a non-profit. You should consider donating to them so that we can bring private messaging to the masses.
Addendum (2024-05-15)
I’ve been asked by several people about my opinions on other platforms and protocols.Specifically, Matrix. I do not trust the Matrix developers to develop or implement a secure protocol for private messaging.
I don’t have an informed opinion about Signal forks (Session, Molly, etc.). Generally, I don’t review cryptography software for FOSS maximalists with skewed threat models unless I’m being paid to do so, and that hasn’t happened yet.
https://soatok.blog/2024/05/14/its-time-for-furries-to-stop-using-telegram/
#endToEndEncryption #furries #FurryFandom #privacy #Signal #Telegram
Update (2024-06-06): There is an update on this project.
As Twitter’s new management continues to nosedive the platform directly into the ground, many people are migrating to what seem like drop-in alternatives; i.e. Cohost and Mastodon. Some are even considering new platforms that none of us have heard of before (one is called “Hive”).
Needless to say, these are somewhat chaotic times.
One topic that has come up several times in the past few days, to the astonishment of many new Mastodon users, is that Direct Messages between users aren’t end-to-end encrypted.
And while that fact makes Mastodon DMs no less safe than Twitter DMs have been this whole time, there is clearly a lot of value and demand in deploying end-to-end encryption for ActivityPub (the protocol that Mastodon and other Fediverse software uses to communicate).
However, given that Melon Husk apparently wants to hurriedly ship end-to-end encryption (E2EE) in Twitter, in some vain attempt to compete with Signal, I took it upon myself to kickstart the E2EE effort for the Fediverse.
https://twitter.com/elonmusk/status/1519469891455234048
So I’d like to share my thoughts about E2EE, how to design such a system from the ground up, and why the direction Twitter is heading looks to be security theater rather than serious cryptographic engineering.
If you’re not interested in those things, but are interested in what I’m proposing for the Fediverse, head on over to the GitHub repository hosting my work-in-progress proposal draft as I continue to develop it.
How to Quickly Build E2EE
If one were feeling particularly cavalier about your E2EE designs, they could just generate then dump public keys through a server they control, pass between users, and have them encrypt client-side. Over and done. Check that box.
Every public key would be ephemeral and implicitly trusted, and the threat model would mostly be, “I don’t want to deal with law enforcement data requests.”
Hell, I’ve previously written an incremental blog post to teach developers about E2EE that begins with this sort of design. Encrypt first, ratchet second, manage trust relationships on public keys last.
If you’re catering to a slightly tech-savvy audience, you might throw in SHA256(pk1 + pk2) -> hex2dec() and call it a fingerprint / safety number / “conversation key” and not think further about this problem.
Look, technical users can verify out-of-band that they’re not being machine-in-the-middle attacked by our service.An absolute fool who thinks most people will ever do this
From what I’ve gathered, this appears to be the direction that Twitter is going.
https://twitter.com/wongmjane/status/1592831263182028800
Now, if you’re building E2EE into a small hobby app that you developed for fun (say: a World of Warcraft addon for erotic roleplay chat), this is probably good enough.
If you’re building a private messaging feature that is intended to “superset Signal” for hundreds of millions of people, this is woefully inadequate.
https://twitter.com/elonmusk/status/1590426255018848256
Art: LvJ
If this is, indeed, the direction Musk is pushing what’s left of Twitter’s engineering staff, here is a brief list of problems with what they’re doing.
- Twitter Web. How do you access your E2EE DMs after opening Twitter in your web browser on a desktop computer?
- If you can, how do you know twitter.com isn’t including malicious JavaScript to snarf up your secret keys on behalf of law enforcement or a nation state with a poor human rights record?
- If you can, how are secret keys managed across devices?
- If you use a password to derive a secret key, how do you prevent weak, guessable, or reused passwords from weakening the security of the users’ keys?
- If you cannot, how do users decide which is their primary device? What if that device gets lost, stolen, or damaged?
- Authenticity. How do you reason about the person you’re talking with?
- Forward Secrecy. If your secret key is compromised today, can you recover from this situation? How will your conversation participants reason about your new Conversation Key?
- Multi-Party E2EE. If a user wants to have a three-way E2EE DM with the other members of their long-distance polycule, does Twitter enable that?
- How are media files encrypted in a group setting? If you fuck this up, you end up like Threema.
- Is your group key agreement protocol vulnerable to insider attacks?
- Cryptography Implementations.
- What does the KEM look like? If you’re using ECC, which curve? Is a common library being used in all devices?
- How are you deriving keys? Are you just using the result of an elliptic curve (scalar x point) multiplication directly without hashing first?
- Independent Third-Party Review.
- Who is reviewing your protocol designs?
- Who is reviewing your cryptographic primitives?
- Who is reviewing the code that interacts with E2EE?
- Is there even a penetration test before the feature launches?
As more details about Twitter’s approach to E2EE DMs come out, I’m sure the above list will be expanded with even more questions and concerns.
My hunch is that they’ll reuse liblithium (which uses Curve25519 and Gimli) for Twitter DMs, since the only expert I’m aware of in Musk’s employ is the engineer that developed that library for Tesla Motors. Whether they’ll port it to JavaScript or just compile to WebAssembly is hard to say.
How To Safely Build E2EE
You first need to decompose the E2EE problem into five separate but interconnected problems.
- Client-Side Secret Key Management.
- Multi-device support
- Protect the secret key from being pilfered (i.e. by in-browser JavaScript delivered from the server)
- Public Key Infrastructure and Trust Models.
- TOFU (the SSH model)
- X.509 Certificate Authorities
- Certificate/Key/etc. Transparency
- SigStore
- PGP’s Web Of Trust
- Key Agreement.
- While this is important for 1:1 conversations, it gets combinatorially complex when you start supporting group conversations.
- On-the-Wire Encryption.
- Direct Messages
- Media Attachments
- Abuse-resistance (i.e. message franking for abuse reporting)
- The Construction of the Previous Four.
- The vulnerability of most cryptographic protocols exists in the joinery between the pieces, not the pieces themselves. For example, Matrix.
This might not be obvious to someone who isn’t a cryptography engineer, but each of those five problems is still really hard.
To wit: The latest IETF RFC draft for Message Layer Security, which tackles the Key Agreement problem above, clocks in at 137 pages.
Additionally, the order I specified these problems matters; it represents my opinion of which problem is relatively harder than the others.
When Twitter’s CISO, Lea Kissner, resigned, they lost a cryptography expert who was keenly aware of the relative difficulty of the first problem.
https://twitter.com/LeaKissner/status/1592937764684980224
You may also notice the order largely mirrors my previous guide on the subject, in reverse. This is because teaching a subject, you start with the simplest and most familiar component. When you’re solving problems, you generally want the opposite: Solve the hardest problems first, then work towards the easier ones.
This is precisely what I’m doing with my E2EE proposal for the Fediverse.
The Journey of a Thousand Miles Begins With A First Step
Before you write any code, you need specifications.
Before you write any specifications, you need a threat model.
Before you write any threat models, you need both a clear mental model of the system you’re working with and how the pieces interact, and a list of security goals you want to achieve.
Less obviously, you need a specific list of non-goals for your design: Properties that you will not prioritize. A lot of security engineering involves trade-offs. For example: elliptic curve choice for digital signatures is largely a trade-off between speed, theoretical security, and real-world implementation security.
If you do not clearly specify your non-goals, they still exist implicitly. However, you may find yourself contradicting them as you change your mind over the course of development.
Being wishy-washy about your security goals is a good way to compromise the security of your overall design.
In my Mastodon E2EE proposal document, I have a section called Design Tenets, which states the priorities used to make trade-off decisions. I chose Usability as the highest priority, because of AviD’s Rule of Usability.
Security at the expense of usability comes at the expense of security.Avi Douglen, Security StackExchange
Underneath Tenets, I wrote Anti-Tenets. These are things I explicitly and emphatically do not want to prioritize. Interoperability with any incumbent designs (OpenPGP, Matrix, etc.) is the most important anti-tenet when it comes to making decisions. If our end-state happens to interop with someone else’s design, cool. I’m not striving for it though!
Finally, this section concludes with a more formal list of Security Goals for the whole project.
Art: LvJ
Every component (from the above list of five) in my design will have an additional dedicated Security Goals section and Threat Model. For example: Client-Side Secret Key Management.
You will then need to tackle each component independently. The threat model for secret-key management is probably the trickiest. The actual encryption of plaintext messages and media attachments is comparatively simple.
Finally, once all of the pieces are laid out, you have the monumental (dare I say, mammoth) task of stitching them together into a coherent, meaningful design.
If you did your job well at the outset, and correctly understand the architecture of the distributed system you’re working with, this will mostly be straightforward.
Making Progress
At every step of the way, you do need to stop and ask yourself, “If I was an absolute chaos gremlin, how could I fuck with this piece of my design?” The more pieces your design has, the longer the list of ways to attack it will grow.
It’s also helpful to occasionally consider formal methods and security proofs. This can have surprising implications for how you use some algorithms.
You should also be familiar enough with the cryptographic primitives you’re working with before you begin such a journey; because even once you’ve solved the key management story (problems 1, 2 and 3 from the above list of 5), cryptographic expertise is still necessary.
- If you’re feeding data into a hash function, you should also be thinking about domain separation. More information.
- If you’re feeding data into a MAC or signature algorithm, you should also be thinking about canonicalization attacks. More information.
- If you’re encrypting data, you should be thinking about multi-key attacks and confused deputy attacks. Also, the cryptographic doom principle if you’re not using IND-CCA3 algorithms.
- At a higher-level, you should proactively defend against algorithm confusion attacks.
How Do You Measure Success?
It’s tempting to call the project “done” once you’ve completed your specifications and built a prototype, and maybe even published a formal proof of your design, but you should first collect data on every important metric:
- How easy is it to use your solution?
- How hard is it to misuse your solution?
- How easy is it to attack your solution? Which attackers have the highest advantage?
- How stable is your solution?
- How performant is your solution? Are the slow pieces the deliberate result of a trade-off? How do you know the balance was struck corectly?
Where We Stand Today
I’ve only begun writing my proposal, and I don’t expect it to be truly ready for cryptographers or security experts to review until early 2023.
However, my clearly specified tenets and anti-tenets were already useful in discussing my proposal on the Fediverse.
@soatok @fasterthanlime Should probably embed the algo used for encryption in the data used for storing the encrypted blob, to support multiples and future changes.@fabienpenso@hachyderm.io proposes in-band protocol negotiation instead of versioned protocols
The main things I wanted to share today are:
- The direction Twitter appears to be heading with their E2EE work, and why I think it’s a flawed approach
- Designing E2EE requires a great deal of time, care, and expertise; getting to market quicker at the expense of a clear and careful design is almost never the right call
Mastodon? ActivityPub? Fediverse? OMGWTFBBQ!
In case anyone is confused about Mastodon vs ActivityPub vs Fediverse lingo:
The end goal of my proposal is that I want to be able to send DMs to queer furries that use Mastodon such that only my recipient can read them.
Achieving this end goal almost exclusively requires building for ActivityPub broadly, not Mastodon specifically.
However, I only want to be responsible for delivering this design into the software I use, not for every single possible platform that uses ActivityPub, nor all the programming languages they’re written in.
I am going to be aggressive about preventing scope creep, since I’m doing all this work for free. (I do have a Ko-Fi, but I won’t link to it from here. Send your donations to the people managing the Mastodon instance that hosts your account instead.)
My hope is that the design documents and technical specifications become clear enough that anyone can securely implement end-to-end encryption for the Fediverse–even if special attention needs to be given to the language-specific cryptographic libraries that you end up using.
Art: LvJ
Why Should We Trust You to Design E2EE?
This sort of question comes up inevitably, so I’d like to tackle it preemptively.
My answer to every question that begins with, “Why should I trust you” is the same: You shouldn’t.
There are certainly cryptography and cybersecurity experts that you will trust more than me. Ask them for their expert opinions of what I’m designing instead of blanketly trusting someone you don’t know.
I’m not interested in revealing my legal name, or my background with cryptography and computer security. Credentials shouldn’t matter here.
If my design is good, you should be able to trust it because it’s good, not because of who wrote it.
If my design is bad, then you should trust whoever proposes a better design instead. Part of why I’m developing it in the open is so that it may be forked by smarter engineers.
Knowing who I am, or what I’ve worked on before, shouldn’t enter your trust calculus at all. I’m a gay furry that works in the technology industry and this is what I’m proposing. Take it or leave it.
Why Not Simply Rubber-Stamp Matrix Instead?
(This section was added on 2022-11-29.)
There’s a temptation, most often found in the sort of person that comments on the /r/privacy subreddit, to ask why even do all of this work in the first place when Matrix already exists?
The answer is simple: I do not trust Megolm, the protocol designed for Matrix.
Megolm has benefited from amateur review for four years. Non-cryptographers will confuse this observation with the proposition that Matrix has benefited from peer review for four years. Those are two different propositions.
In fact, the first time someone with cryptography expertise bothered to look at Matrix for more than a glance, they found critical vulnerabilities in its design. These are the kinds of vulnerabilities that are not easily mitigated, and should be kept in mind when designing a new protocol.
You don’t have to take my word for it. Listen to the Security, Cryptography, Whatever podcast episode if you want cryptographic security experts’ takes on Matrix and these attacks.
From one of the authors of the attack paper:
So they kind of, after we disclosed to them, they shared with us their timeline. It’s not fixed yet. It’s a, it’s a bigger change because they need to change the protocol. But they always said like, Okay, fair enough, they’re gonna change it. And they also kind of announced a few days after kind of the public disclosure based on the public reaction that they should prioritize fixing that. So it seems kind of in the near future, I don’t have the timeline in front of me right now. They’re going to fix that in the sense of like the— because there’s, notions of admins and so on. So like, um, so authenticating such group membership requests is not something that is kind of completely outside of, kind of like the spec. They just kind of need to implement the appropriate authentication and cryptography.Martin Albrecht, SCW podcast
From one of the podcast hosts:
I guess we can at the very least tell anyone who’s going forward going to try that, that like, yes indeed. You should have formal models and you should have proofs. And so there’s this, one of the reactions to kind of the kind of attacks that we presented and also to prior previous work where we kind of like broken some cryptographic protocols is then to say like, “Well crypto’s hard”, and “don’t roll your own crypto.” But in a way the thing is like, you know, we need some people to roll their own crypto because that’s how we have crypto. Someone needs to roll it. But we have developed techniques, we have developed formalisms, we have developed methods for making sure it doesn’t have to be hard, it’s not, it’s not a dark art kind of that only kind of a few, a select few can master, but it’s, you know, it’s a science and you can learn it. So, but you need to then indeed employ a cryptographer in kind of like forming, modeling your protocol and whenever you make changes, then, you know, they need to look over this and say like, Yes, my proof still goes through. Um, so like that is how you do this. And then, then true engineering is still hard and it will remain hard and you know, any science is hard, but then at least you have some confidence in what you’re doing. You might still then kind of on the space and say like, you know, the attack surface is too large and I’m not gonna to have an encrypted backup. Right. That’s then the problem of a different hard science, social science. Right. But then just use the techniques that we have, the methods that we have to establish what we need.Thomas Ptacek, SCW podcast
It’s tempting to listen to these experts and say, “OK, you should use libsignal instead.”
But libsignal isn’t designed for federation and didn’t prioritize group messaging. The UX for Signal is like an IM application between two parties. It’s a replacement for SMS.
It’s tempting to say, “Okay, but you should use MLS then; never roll your own,” but MLS doesn’t answer the group membership issue that plagued Matrix. It punts on these implementation details.
Even if I use an incumbent protocol that privacy nerds think is good, I’ll still have to stitch it together in a novel manner. There is no getting around this.
Maybe wait until I’ve finished writing the specifications for my proposal before telling me I shouldn’t propose anything.
Credit for art used in header: LvJ, Harubaki
https://soatok.blog/2022/11/22/towards-end-to-end-encryption-for-direct-messages-in-the-fediverse/
In late 2022, I blogged about the work needed to develop a specification for end-to-end encryption for the fediverse. I sketched out some of the key management components on GitHub, and then the public work abruptly stalled.A few of you have wondered what’s the deal with that.
This post covers why this effort stalled, what I’m proposing we do next.
What’s The Hold Up?
The “easy” (relatively speaking) parts of the problem are as follows:
- Secret key management. (This is sketched out already, and provides multiple mechanisms for managing secret key material. Yay!)
- Bulk encryption of messages and media. (I’ve done a lot of work in this space over the years, so it’s an area I’m deeply familiar with. When we get to this part, it will be almost trivial. I’m not worried about it at all.)
- Forward-secure ratcheting / authenticated key exchange / group key agreement. (RFC 9420 is a great starting point.)
That is to say, managing secret keys, using secret keys, and deriving shared secret keys are all in the “easy” bucket.
The hard part? Public key management.
CMYKat made this
Why is Public Key Management Hard?
In a centralized service (think: Twitter, Facebook, etc.), this is actually much easier to build: Shove your public keys into a database, and design your client-side software to trust whatever public key your server gives them. Bob’s your uncle, pack it up and go home.Unfortunately, it’s kind of stupid to build anything that way.
If you explicitly trust the server, the server could provide the wrong public key (i.e., one for which the server knows the corresponding secret key) and you’ll be none the wiser. This makes it trivial for the server to intercept and read your messages.
If your users are trusting you regardless, they’re probably just as happy if you don’t encrypt at the endpoint at all (beyond using TLS, but transport encryption is table stakes for any online service so nevermind that).
But let’s say you wanted to encrypt between peers anyway, because you’re feeling generous (or don’t want to field a bunch of questionably legal demands for user data by law enforcement; a.k.a. the Snapchat threat model).
You could improve endpoint trust by shoving all of your users’ public keys into an append-only data structure; i.e. key transparency, like WhatsApp proposed in 2023:
https://www.youtube.com/watch?v=_N4Q05z5vPE
And, to be perfectly clear, key transparency is a damn good idea.
Key transparency keeps everyone honest and makes it difficult for criminals to secretly replace a victim’s public key, because the act of doing so is unavoidably published to an append-only log.
The primary challenge is scaling a transparency feature to serve a public, federated system.
Federated Key Transparency?
Despite appearances, I haven’t been sitting on my thumbs for the past year or so. I’ve been talking with cryptography experts about their projects and papers in the same space.Truthfully, I had been hoping to piggyback off one of those upcoming projects (which is focused more on public key discovery for SAML- and OAuth-like protocols) to build the Federated PKI piece for E2EE for the Fediverse.
Unfortunately, that project keeps getting delayed and pushed back, and I’ve just about run out of patience for it.
Additionally, there are some engineering challenges that I would need to tackle to build atop it, so it’s not as simple as “let’s just use that protocol”, either.
So let’s do something else instead:
Art: ScruffKerfluff
Fediverse Public Key Directories
Orthogonal to the overall Fediverse E2EE specification project, let’s build a Public Key Directory for the Fediverse.This will not only be useful for building a coherent specification for E2EE (as it provides the “Federated PKI” component we’d need to build it securely), but it would also be extremely useful for software developers the whole world over.
Imagine this:
- If you want to fetch a user’s SSH public key, you can just query for their username and get a list of non-expired, non-revoked public keys to choose from.
- If you wanted public key pinning and key rotation for OAuth2 and/or OpenID Connect identity providers without having to update configurations or re-deploy any applications, you can do that.
- If you want to encrypt a message to a complete stranger, such that only they can decrypt it, without any sort of interaction (i.e., they could be offline for a holiday and still decrypt it when they get back), you could do that.
Oh, and best of all? You can get all these wins without propping up any cryptocurrency bullshit either.
From simple abstractions, great power may bloom.Mark Miller
How Will This Work?
We need to design a specific kind of server that speaks a limited set of the ActivityPub protocol.I say “limited” because it will only not support editing or deleting messages provided by another instance. It will only append data.
To understand the full picture, let’s first look at the message types, public key types, and how the message types will be interpreted.
Message Types
Under the ActivityPub layer, we will need to specify a distinct set of Directory Message Types. An opening offer would look like this:
[b]AddKey[/b]
— contains an Asymmetric Public Key, a number mapped to the user, and instance that hosts it, and some other metadata (i.e., time)[b]RevokeKey[/b]
— marks an existing public key as revoked[b]MoveIdentity[/b]
— moves all of the public keys from identity A to identity B. This can be used for username changes or instance migrations.We may choose to allow more message types at the front-end if need be, but that’s enough for our purposes.
Public Key Types
We are not interested in backwards compatibility with every existing cryptosystem. We will only tolerate a limited set of public key types.At the outset, only Ed25519 will be supported.
In the future, we will include post-quantum digital signature algorithms on this list, but not before the current designs have had time to mature.
RSA will never be included in the set.
ECDSA over NIST P-384 may be included at some point, if there’s sufficient interest in supporting e.g., US government users.
If ECDSA is ever allowed, RFC 6979 is mandatory.
Message Processing
When an instance sends a message to a Directory Server, it will need to contain a specific marker for our protocol. Otherwise, it will be rejected.Each message will have its own processing rules.
After the processing rules are applied, the message will be stored in the Directory Server, and a hash of the message will be published to a SigSum transparency ledger. The Merkle root and inclusion proofs will be stored in an associated record, attached to the record for the new message.
Every message will have its hash published in SigSum. No exceptions.
We will also need a mechanism for witness co-signatures to be published and attached to the record.
Additionally, all messages defined here are generated by the users, client-side. Servers are not trusted, generally, as part of the overall E2EE threat model.
AddKey
{ "@context": "https://example.com/ns/fedi-e2ee/v1", "action": "AddKey", "message": { "time": "2024-12-31T23:59:59Z", "identity": "foo@mastodon.example.com", "public-key": "ed25519:<key goes here>" }, "signature": "SignatureOfMessage"}The first
AddKey
for any given identity will need to be self-signed by the key being added (in addition to ActivityPub messages being signed by the instance).After an identity exists in the directory, every subsequent public key MUST be signed by a non-revoked keypair.
RevokeKey
{ "@context": "https://example.com/ns/fedi-e2ee/v1", "action": "RevokeKey", "message": { "time": "2024-12-31T23:59:59Z", "identity": "foo@mastodon.example.com", "public-key": "ed25519:<key goes here>" }, "signature": "SignatureOfMessage"}This marks the public key as untrusted, and effectively “deletes” it from the list that users will fetch.
Important: RevokeKey will fail unless there is at least one more trusted public key for this user. Otherwise, a denial of service would be possible.
Replaying an AddKey for a previously-revoked key MUST fail.
MoveIdentity
{ "@context": "https://example.com/ns/fedi-e2ee/v1", "action": "MoveIdentity", "message": { "time": "2024-12-31T23:59:59Z", "old-identity": "foo@mastodon.example.com", "new-identity": "bar@akko.example.net" }, "signature": "SignatureOfMessage"}This exists to facilitate migrations and username changes.
Other Message Types
The above list is not exhaustive. We may need other message types depending on the exact feature set needed by the final specification.Fetching Public Keys
A simple JSON API (and/or an ActivityStream; haven’t decided) will be exposed to query for the currently trusted public keys for a given identity.{ "@context": "https://example.com/ns/fedi-e2ee/v1", "public-keys": [ { "data": { "time": "2024-12-31T23:59:59Z", "identity": "foo@mastodon.example.com", "public-key": "ed25519:<key goes here>" }, "signature": "SignatureOfData", "sigsum": { /* ... */ }, }, { "data": { /* ... */ }, /* ... */ }, /* ... */ ]}
Simple and easy.
Gossip Between Instances
Directory Servers should be configurable to mirror records from other instances.Additionally, they should be configurable to serve as Witnesses for the SigSum protocol.
The communication layer here between Directory Servers will also be ActivityPub.
Preventing Abuse
The capability of learning a user’s public key doesn’t imply the ability to send messages or bypass their block list.Additionally, Fediverse account usernames are (to my knowledge) generally not private, so I don’t anticipate there being any danger in publishing public keys to an append-only ledger.
That said, I am totally open to considering use cases where the actual identity is obfuscated (e.g., HMAC with a static key known only to the instance that hosts them instead of raw usernames).
What About GDPR / Right To Be Forgotten?
Others have previously suggested that usernames might be subject to the “right to be forgotten”, which would require breaking history for an append-only ledger.After discussing a proposed workaround with a few people in the Signal group for this project, we realized complying necessarily introduced security issues by giving instance admins the capability of selectively remapping the user ID to different audiences, and detecting/mitigating this remapping is annoying.
However, we don’t need to do that in the first place.
According to this webpage about GDPR’s Right to be Forgotten:
However, an organization’s right to process someone’s data might override their right to be forgotten. Here are the reasons cited in the GDPR that trump the right to erasure:
- (…)
- The data is being used to perform a task that is being carried out in the public interest or when exercising an organization’s official authority.
- (…)
- The data represents important information that serves the public interest, scientific research, historical research, or statistical purposes and where erasure of the data would likely to impair or halt progress towards the achievement that was the goal of the processing.
Enabling private communication is in the public interest. The only information that will be stored in the ledger in relation to the username are cryptographic public keys, so it’s not like anything personal (e.g., email addresses or legal names) will be included.However, we still need to be extremely up-front about this to ensure EU citizens are aware of the trade-off we’re making.
Account Recovery
In the event that a user loses access to all of their secret keys and wants to burn down the old account, they may want a way to start over with another fresh self-signedAddKey
.However, the existing policies I wrote above would make this challenging:
- Since every subsequent
AddKey
must be signed by an incumbent key, if you don’t have access to these secret keys, you’re locked out.- Since
RevokeKey
requires one trusted keypair remains in the set, for normal operations, you can’t just burn the set down to zero even while you still had access to the secret keys.There is an easy way out of this mess: Create a new verb; e.g.
BurnDown
that an instance can issue that resets all signing keys for a given identity.The use of
BurnDown
should be a rare, exceptional event that makes a lot of noise:
- All existing E2EE sessions must break, loudly.
- All other participants must be alerted to the change, through the client software.
- Witnesses and watchdog nodes must take note of this change.
This comes with some trade-offs. Namely: Any account recovery mechanism is a backdoor, and giving the instance operators the capability of issuing
BurnDown
messages is a risk to their users.Therefore, users who trust their own security posture and wish to opt out of this recovery feature should also be able to issue a
Fireproof
message at any point in the process, which permanent and irrevocably prevents anyBurnDown
from being accepted on their current instance.If users opt out of recovery and then lose their signing keys, they’re locked out and need to start over with a new Fediverse identity. On the flipside, their instance operator cannot successfully issue a BurnDown for them, so they have to trust them less.
Notice
This is just a rough sketch of my initial ideas, going into this project. It is not comprehensive, nor complete.There are probably big gaps that need to be filled in, esp. on the ActivityPub side of things. (I’m not as worried about the cryptography side of things.)
How Will This Be Used for E2EE Direct Messaging?
I anticipate that a small pool of Directory Servers will be necessary, due to only public keys and identities being stored.Additional changes beyond just the existence of Directory Servers will need to be made to facilitate private messaging. Some of those changes include:
- Some endpoint for users to know which Directory Servers a given ActivityPub instance federates with (if any).
- Some mechanism for users to asynchronously exchange Signed Pre-Key bundles for initiating contact. (One for users to publish new bundles, another for users to retrieve a bundle.)
- These will be Ed25519-signed payloads containing an ephemeral X25519 public key.
This is all outside the scope of the proposal I’m sketching out here today, but it’s worth knowing that I’m aware of the implementation complexity.
The important thing is: I (soatok@furry.engineer) should be able to query pawb.fun, find the Directory Server(s) they federate with, and then query that Directory server for
Crashdoom@pawb.fun
and get his currently trusted Ed25519 public keys.From there, I can query pawb.fun for a SignedPreKey bundle, which will have been signed by one of those public keys.
And then we can return to the “easy” pile.
Development Plan
Okay, so that was a lot of detail, and yet not enough detail, depending on who’s reading this blog post.What I wrote here today is a very rough sketch. The devil is always in the details, especially with cryptography.
Goals and Non-Goals
We want Fediverse users to be able to publish a public key that is bound to their identity, which anyone else on the Internet can fetch and then use for various purposes.We want to leverage the existing work into key transparency by the cryptography community.
We don’t want to focus on algorithm agility or protocol compatibility.
We don’t want to involve any government offices in the process. We don’t care about “real” identities, nor about codifying falsehoods about names.
We don’t want any X.509 or Web-of-Trust machinery involved in the process.
Tasks
The first thing we would need to do is write a formal specification for a Directory Server (whose job is only to vend Public Keys in an auditable, transparent manner).Next, we need to actually build a reference implementation of this server, test it thoroughly, and then have security experts pound at the implementation for a while. Any security issues that can be mitigated by design will require a specification update.
We will NOT punt these down to implementors to be responsible for, unless we cannot avoid doing so.
Once these steps are done, we can start rolling the Directory Servers out. At this point, we can develop client-side libraries in various programming languages to make it easy for developers to adopt.My continued work on the E2EE specification for the Fediverse can begin after we have an implementation of the Directory Server component ready to go.
Timeline
I have a very demanding couple of months ahead of me, professionally, so I don’t yet know when I can commit to starting the Fediverse Directory Server specification work.Strictly speaking, it’s vaguely possible to get buy-in from work to focus on this project as part of my day-to-day responsibilities, since it has immediate and lasting value to the Internet.However, I don’t want to propose it because that would be crossing the professional-personal streams in a way I’m not really comfortable with.
The last thing I need is angry Internet trolls harassing my coworkers to try to get under my fur, y’know?
If there is enough interest from the broader Fediverse community, I’m also happy to delegate this work to anyone interested.Once the work can begin, I don’t anticipate it will take more than a week for me to write a specification that other crypto nerds will take seriously.
I am confident in this because most of the cryptography will be constrained to hash functions, preventing canonicalization and cross-protocol attacks, and signatures.
Y’know, the sort of thing I write about on my furry blog for fun!
Building a reference implementation will likely take a bit longer; if, for no other reason, than I believe it would be best to write it in Go (which has the strongest SigSum support, as of this writing).
This is a lot of words to say, as far as timelines go:
How to Get Involved
Regardless of whether my overall E2EE proposal gets adopted, the Directory Server component is something that should be universally useful to the Fediverse and to software developers around the world.If you are interested in participating in any technical capacity, I have just created a Signal Group for discussing and coordinating efforts.
All of these efforts will also be coordinated on the fedi-e2ee GitHub organization.
The public key directory server’s specification will eventually exist in this GitHub repository.
Can I Contribute Non-Technically?
Yes, absolutely. In the immediate future, once it kicks off, the work is going to be technology-oriented.However, we may need people with non-technical skills at some point, so feel free to dive in whenever you feel comfortable.
What About Financially?
If you really have money burning a hole in your pocket and want to toss a coin my way, I do have a Ko-Fi. Do not feel pressured at all to do so, however.Because I only use Ko-Fi as a tip jar, rather than as a business, I’m not specifically tracking which transaction is tied to which project, so I can’t make any specific promises about how any of the money sent my way will be allocated.
What I will promise, however, is that any icons/logos/etc. created for this work will be done by an artist and they will be adequately compensated for their work. I will not use large-scale computing (a.k.a., “Generative AI”) for anything.
Closing Thoughts
What I’ve sketched here is much simpler (and more ActivityPub-centric) than the collaboration I was originally planning.Thanks for being patient while I tried, in vain, to make that work.
As of today, I no longer think we need to wait for them. We can build this ourselves, for each other.
https://soatok.blog/2024/06/06/towards-federated-key-transparency/
#cryptography #endToEndEncryption #fediverse #KeyTransparency #Mastodon #MerkleTrees #PublicKeys
Governments are back on their anti-encryption bullshit again.
Between the U.S. Senate’s “EARN IT” Act, the E.U.’s slew of anti-encryption proposals, and Australia’s new anti-encryption law, it’s become clear that the authoritarians in office view online privacy as a threat to their existence.
Normally, when the governments increase their anti-privacy sabre-rattling, technologists start talking more loudly about Tor, Signal, and other privacy technologies (usually only to be drowned out by paranoid people who think Tor and Signal are government backdoors or something stupid; conspiracy theories ruin everything!).
I’m not going to do that.
Instead, I’m going to show you how to add end-to-end encryption to any communication software you’re developing. (Hopefully, I’ll avoid making any bizarre design decisions along the way.)
But first, some important disclaimers:
- Yes, you should absolutely do this. I don’t care how banal your thing is; if you expect people to use it to communicate with each other, you should make it so that you can never decrypt their communications.
- You should absolutely NOT bill the thing you’re developing as an alternative to Signal or WhatsApp.
- The goal of doing this is to increase the amount of end-to-end encryption deployed on the Internet that the service operator cannot decrypt (even if compelled by court order) and make E2EE normalized. The goal is NOT to compete with highly specialized and peer-reviewed privacy technology.
- I am not a lawyer, I’m some furry who works in cryptography. The contents of this blog post is not legal advice, nor is it endorsed by any company or organization. Ask the EFF for legal questions.
The organization of this blog post is as follows: First, I’ll explain how to encrypt and decrypt data between users, assuming you have a key. Next, I’ll explain how to build an authenticated key exchange and a ratcheting protocol to determine the keys used in the first step. Afterwards, I’ll explore techniques for binding authentication keys to identities and managing trust. Finally, I’ll discuss strategies for making it impractical to ever backdoor your software (and impossible to silently backdoor it), just to piss the creeps and tyrants of the world off even more.
You don’t have to implement the full stack of solutions to protect users, but the further you can afford to go, the safer your users will be from privacy-invasive policing.
(Art by Kyume.)
Preliminaries
Choosing a Cryptography Library
In the examples contained on this page, I will be using the Sodium cryptography library. Specifically, my example code will be written with the Sodium-Plus library for JavaScript, since it strikes a good balance between performance and being cross-platform.
const { SodiumPlus } = require('sodium-plus');(async function() { // Select a backend automatically const sodium = await SodiumPlus.auto(); // Do other stuff here})();
Libsodium is generally the correct choice for developing cryptography features in software, and is available in most programming languages,
If you’re prone to choose a different library, you should consult your cryptographer (and yes, you should have one on your payroll if you’re doing things different) about your design choices.
Threat Modelling
Remember above when I said, “You don’t have to implement the full stack of solutions to protect users, but the further you can afford to go, the safer your users will be from privacy-invasive policing”?
How far you go in implementing the steps outlined on this blog post should be informed by a threat model, not an ad hoc judgment.
For example, if you’re encrypting user data and storing it in the cloud, you probably want to pass the Mud Puddle Test:
1. First, drop your device(s) in a mud puddle.
2. Next, slip in said puddle and crack yourself on the head. When you regain consciousness you’ll be perfectly fine, but won’t for the life of you be able to recall your device passwords or keys.
3. Now try to get your cloud data back.Did you succeed? If so, you’re screwed. Or to be a bit less dramatic, I should say: your cloud provider has access to your ‘encrypted’ data, as does the government if they want it, as does any rogue employee who knows their way around your provider’s internal policy checks.
Matthew Green describes the Mud Puddle Test, which Apple products definitely don’t pass.
If you must fail the Mud Puddle Test for your users, make sure you’re clear and transparent about this in the documentation for your product or service.
(Art by Swizz.)
I. Symmetric-Key Encryption
The easiest piece of this puzzle is to encrypt data in transit between both ends (thus, satisfying the loosest definition of end-to-end encryption).
At this layer, you already have some kind of symmetric key to use for encrypting data before you send it, and for decrypting it as you receive it.
For example, the following code will encrypt/decrypt strings and return hexadecimal strings with a version prefix.
const VERSION = "v1";/** * @param {string|Uint8Array} message * @param {Uint8Array} key * @param {string|null} assocData * @returns {string} */async function encryptData(message, key, assocData = null) { const nonce = await sodium.randombytes_buf(24); const aad = JSON.stringify({ 'version': VERSION, 'nonce': await sodium.sodium_bin2hex(nonce), 'extra': assocData }); const encrypted = await sodium.crypto_aead_xchacha20poly1305_ietf_encrypt( message, nonce, key, aad ); return ( VERSION + await sodium.sodium_bin2hex(nonce) + await sodium.sodium_bin2hex(encrypted) );}/** * @param {string|Uint8Array} message * @param {Uint8Array} key * @param {string|null} assocData * @returns {string} */async function decryptData(encrypted, key, assocData = null) { const ver = encrypted.slice(0, 2); if (!await sodium.sodium_memcmp(ver, VERSION)) { throw new Error("Incorrect version: " + ver); } const nonce = await sodium.sodium_hex2bin(encrypted.slice(2, 50)); const ciphertext = await sodium.sodium_hex2bin(encrypted.slice(50)); const aad = JSON.stringify({ 'version': ver, 'nonce': encrypted.slice(2, 50), 'extra': assocData }); const plaintext = await sodium.crypto_aead_xchacha20poly1305_ietf_decrypt( ciphertext, nonce, key, aad ); return plaintext.toString('utf-8');}
Under-the-hood, this is using XChaCha20-Poly1305, which is less sensitive to timing leaks than AES-GCM. However, like AES-GCM, this encryption mode doesn’t provide message- or key-commitment.
If you want key commitment, you should derive two keys from $key
using a KDF based on hash functions: One for actual encryption, and the other as a key commitment value.
If you want message commitment, you can use AES-CTR + HMAC-SHA256 or XChaCha20 + BLAKE2b-MAC.
If you want both, ask Taylor Campbell about his BLAKE3-based design.
A modified version of the above code with key-commitment might look like this:
const VERSION = "v2";/** * Derive an encryption key and a commitment hash. * @param {CryptographyKey} key * @param {Uint8Array} nonce * @returns {{encKey: CryptographyKey, commitment: Uint8Array}} */async function deriveKeys(key, nonce) { const encKey = new CryptographyKey(await sodium.crypto_generichash( new Uint8Array([0x01].append(nonce)), key )); const commitment = await sodium.crypto_generichash( new Uint8Array([0x02].append(nonce)), key ); return {encKey, commitment};}/** * @param {string|Uint8Array} message * @param {Uint8Array} key * @param {string|null} assocData * @returns {string} */async function encryptData(message, key, assocData = null) { const nonce = await sodium.randombytes_buf(24); const aad = JSON.stringify({ 'version': VERSION, 'nonce': await sodium.sodium_bin2hex(nonce), 'extra': assocData }); const {encKey, commitment} = await deriveKeys(key, nonce); const encrypted = await sodium.crypto_aead_xchacha20poly1305_ietf_encrypt( message, nonce, encKey, aad ); return ( VERSION + await sodium.sodium_bin2hex(nonce) + await sodium.sodium_bin2hex(commitment) + await sodium.sodium_bin2hex(encrypted) );}/** * @param {string|Uint8Array} message * @param {Uint8Array} key * @param {string|null} assocData * @returns {string} */async function decryptData(encrypted, key, assocData = null) { const ver = encrypted.slice(0, 2); if (!await sodium.sodium_memcmp(ver, VERSION)) { throw new Error("Incorrect version: " + ver); } const nonce = await sodium.sodium_hex2bin(encrypted.slice(2, 50)); const ciphertext = await sodium.sodium_hex2bin(encrypted.slice(114)); const aad = JSON.stringify({ 'version': ver, 'nonce': encrypted.slice(2, 50), 'extra': assocData }); const storedCommitment = await sodium.sodium_hex2bin(encrypted.slice(50, 114)); const {encKey, commitment} = await deriveKeys(key, nonce); if (!(await sodium.sodium_memcmp(storedCommitment, commitment))) { throw new Error("Incorrect commitment value"); } const plaintext = await sodium.crypto_aead_xchacha20poly1305_ietf_decrypt( ciphertext, nonce, encKey, aad ); return plaintext.toString('utf-8');}
Another design choice you might make is to encode ciphertext with base64 instead of hexadecimal. That doesn’t significantly alter the design here, but it does mean your decoding logic has to accommodate this.
You SHOULD version your ciphertexts, and include this in the AAD provided to your AEAD encryption mode. I used “v1” and “v2” as a version string above, but you can use your software name for that too.
II. Key Agreement
If you’re not familiar with Elliptic Curve Diffie-Hellman or Authenticated Key Exhcanges, the two of the earliest posts on this blog were dedicated to those topics.
Key agreement in libsodium uses Elliptic Curve Diffie-Hellman over Curve25519, or X25519 for short.
There are many schools of thought for extending ECDH into an authenticated key exchange protocol.
We’re going to implement what the Signal Protocol calls X3DH instead of doing some interactive EdDSA + ECDH hybrid, because X3DH provides cryptographic deniability (see this section of the X3DH specification for more information).
For the moment, I’m going to assume a client-server model. That may or may not be appropriate for your design. You can substitute “the server” for “the other participant” in a peer-to-peer configuration.
Head’s up: This section of the blog post is code-heavy.
Update (November 23, 2020): I implemented this design in TypeScript, if you’d like something tangible to work with. I call my library, Rawr X3DH.
X3DH Pre-Key Bundles
Each participant will need to upload an Ed25519 identity key once (which is a detail covered in another section), which will be used to sign bundles of X25519 public keys to use for X3DH.
Your implementation will involve a fair bit of boilerplate, like so:
/** * Generate an X25519 keypair. * * @returns {{secretKey: X25519SecretKey, publicKey: X25519PublicKey}} */async function generateKeyPair() { const keypair = await sodium.crypto_box_keypair(); return { secretKey: await sodium.crypto_box_secretkey(keypair), publicKey: await sodium.crypto_box_publickey(keypair) };}/** * Generates some number of X25519 keypairs. * * @param {number} preKeyCount * @returns {{secretKey: X25519SecretKey, publicKey: X25519PublicKey}[]} */async function generateBundle(preKeyCount = 100) { const bundle = []; for (let i = 0; i < preKeyCount; i++) { bundle.push(await generateKeyPair()); } return bundle;}/** * BLAKE2b( len(PK) | PK_0, PK_1, ... PK_n ) * * @param {X25519PublicKey[]} publicKeys * @returns {Uint8Array} */async function prehashPublicKeysForSigning(publicKeys) { const hashState = await sodium.crypto_generichash_init(); // First, update the state with the number of public keys const pkLen = new Uint8Array([ (publicKeys.length >>> 24) & 0xff, (publicKeys.length >>> 16) & 0xff, (publicKeys.length >>> 8) & 0xff, publicKeys.length & 0xff ]); await sodium.crypto_generichash_update(hashState, pkLen); // Next, update the state with each public key for (let pk of publicKeys) { await sodium.crypto_generichash_update( hashState, pk.getBuffer() ); } // Return the finalized BLAKE2b hash return await sodium.crypto_generichash_final(hashState);}/** * Signs a bundle. Returns the signature. * * @param {Ed25519SecretKey} signingKey * @param {X25519PublicKey[]} publicKeys * @returns {Uint8Array} */async function signBundle(signingKey, publicKeys) { return sodium.crypto_sign_detached( await prehashPublicKeysForSigning(publicKeys), signingKey );}/** * This is just so you can see how verification looks. * * @param {Ed25519PublicKey} verificationKey * @param {X25519PublicKey[]} publicKeys * @param {Uint8Array} signature */async function verifyBundle(verificationKey, publicKeys, signature) { return sodium.crypto_sign_verify_detached( await prehashPublicKeysForSigning(publicKeys), verificationKey, signature );}
This boilerplate exists just so you can do something like this:
/** * Generate some number of X25519 keypairs. * Persist the bundle. * Sign the bundle of publickeys with the Ed25519 secret key. * Return the signed bundle (which can be transmitted to the server.) * * @param {Ed25519SecretKey} signingKey * @param {number} numKeys * @returns {{signature: string, bundle: string[]}} */async function x3dh_pre_key(signingKey, numKeys = 100) { const bundle = await generateBundle(numKeys); const publicKeys = bundle.map(x => x.publicKey); const signature = await signBundle(signingKey, publicKeys); // This is a stub; how you persist it is app-specific: persistBundleNotDefinedHere(signingKey, bundle); // Hex-encode all the public keys const encodedBundle = []; for (let pk of publicKeys) { encodedBundle.push(await sodium.sodium_bin2hex(pk.getBuffer())); } return { 'signature': await sodium.sodium_bin2hex(signature), 'bundle': encodedBundle };}
And then you can drop the output of x3dh_pre_key(secretKey)
into a JSON-encoded HTTP request.
In accordance to Signal’s X3DH spec, you want to use x3dh_pre_key(secretKey, 1)
to generate the “signed pre-key” bundle and x3dn_pre_key(secretKey, 100)
when pushing 100 one-time keys to the server.
X3DH Initiation
This section conforms to the Sending the Initial Message section of the X3DH specification.
When you initiate a conversation, the server should provide you with a bundle containing:
- Your peer’s Identity key (an Ed25519 public key)
- Your peer’s current Signed Pre-Key (an X25519 public key)
- (If any remain unburned) One of your key’s One-Time Keys (an X25519 public key) — and then delete it
If we assume the structure of this response looks like this:
{ "IdentityKey": "...", "SignedPreKey": { "Signature": "..." "PreKey": "..." }, "OneTimeKey": "..." // or NULL}
Then we can write the initiation step of the handshake like so:
/** * Get SK for initializing an X3DH handshake * * @param {object} r -- See previous code block * @param {Ed25519SecretKey} senderKey */async function x3dh_initiate_send_get_sk(r, senderKey) { const identityKey = new Ed25519PublicKey( await sodium.sodium_hex2bin(r.IdentityKey) ); const signedPreKey = new X25519PublicKey( await sodium.sodium_hex2bin(r.SignedPreKey.PreKey) ); const signature = await sodium.sodium_hex2bin(r.SignedPreKey.Signature); // Check signature const valid = await verifyBundle(identityKey, [signedPreKey], signature); if (!valid) { throw new Error("Invalid signature"); } const ephemeral = await generateKeyPair(); const ephSecret = ephemeral.secretKey; const ephPublic = ephemeral.publicKey; // Turn the Ed25519 keys into X25519 keys for X3DH: const senderX = await sodium.crypto_sign_ed25519_sk_to_curve25519(senderKey); const recipientX = await sodium.crypto_sign_ed25519_pk_to_curve25519(identityKey); // See the X3DH specification to really understand this part: const DH1 = await sodium.crypto_scalarmult(senderX, signedPreKey); const DH2 = await sodium.crypto_scalarmult(ephSecret, recipientX); const DH3 = await sodium.crypto_scalarmult(ephSecret, signedPreKey); let SK; if (r.OneTimeKey) { let DH4 = await sodium.crypto_scalarmult( ephSecret, new X25519PublicKey(await sodium.sodium_hex2bin(r.OneTimeKey)) ); SK = kdf(new Uint8Array( [].concat(DH1.getBuffer()) .concat(DH2.getBuffer()) .concat(DH3.getBuffer()) .concat(DH4.getBuffer()) )); DH4.wipe(); } else { SK = kdf(new Uint8Array( [].concat(DH1.getBuffer()) .concat(DH2.getBuffer()) .concat(DH3.getBuffer()) )); } // Wipe keys DH1.wipe(); DH2.wipe(); DH3.wipe(); ephSecret.wipe(); senderX.wipe(); return { IK: identityKey, EK: ephPublic, SK: SK, OTK: r.OneTimeKey // might be NULL };}/** * Initialize an X3DH handshake * * @param {string} recipientIdentity - Some identifier for the user * @param {Ed25519SecretKey} secretKey - Sender's secret key * @param {Ed25519PublicKey} publicKey - Sender's public key * @param {string} message - The initial message to send * @returns {object} */async function x3dh_initiate_send(recipientIdentity, secretKey, publicKey, message) { const r = await get_server_response(recipientIdentity); const {IK, EK, SK, OTK} = await x3dh_initiate_send_get_sk(r, secretKey); const assocData = await sodium.sodium_bin2hex( new Uint8Array( [].concat(publicKey.getBuffer()) .concat(IK.getBuffer()) ) ); /* * We're going to set the session key for our recipient to SK. * This might invoke a ratchet. * * Either SK or the output of the ratchet derived from SK * will be returned by getEncryptionKey(). */ await setSessionKey(recipientIdentity, SK); const encrypted = await encryptData( message, await getEncryptionKey(recipientIdentity), assocData ); return { "Sender": my_identity_string, "IdentityKey": await sodium.sodium_bin2hex(publicKey), "EphemeralKey": await sodium.sodium_bin2hex(EK), "OneTimeKey": OTK, "CipherText": encrypted };}
We didn’t define setSessionKey()
or getEncryptionKey()
above. It will be covered later.
X3DH – Receiving an Initial Message
This section implements the Receiving the Initial Message section of the X3DH Specification.
We’re going to assume the structure of the request looks like this:
{ "Sender": "...", "IdentityKey": "...", "EphemeralKey": "...", "OneTimeKey": "...", "CipherText": "..."}
The code to handle this should look like this:
/** * Handle an X3DH initiation message as a receiver * * @param {object} r -- See previous code block * @param {Ed25519SecretKey} identitySecret * @param {Ed25519PublicKey} identityPublic * @param {Ed25519SecretKey} preKeySecret */async function x3dh_initiate_recv_get_sk( r, identitySecret, identityPublic, preKeySecret) { // Decode strings const senderIdentityKey = new Ed25519PublicKey( await sodium.sodium_hex2bin(r.IdentityKey), ); const ephemeral = new X25519PublicKey( await sodium.sodium_hex2bin(r.EphemeralKey), ); // Ed25519 -> X25519 const senderX = await sodium.crypto_sign_ed25519_pk_to_curve25519(senderIdentityKey); const recipientX = await sodium.crypto_sign_ed25519_sk_to_curve25519(identitySecret); // See the X3DH specification to really understand this part: const DH1 = await sodium.crypto_scalarmult(preKeySecret, senderX); const DH2 = await sodium.crypto_scalarmult(recipientX, ephemeral); const DH3 = await sodium.crypto_scalarmult(preKeySecret, ephemeral); let SK; if (r.OneTimeKey) { let DH4 = await sodium.crypto_scalarmult( await fetchAndWipeOneTimeSecretKey(r.OneTimeKey), ephemeral ); SK = kdf(new Uint8Array( [].concat(DH1.getBuffer()) .concat(DH2.getBuffer()) .concat(DH3.getBuffer()) .concat(DH4.getBuffer()) )); DH4.wipe(); } else { SK = kdf(new Uint8Array( [].concat(DH1.getBuffer()) .concat(DH2.getBuffer()) .concat(DH3.getBuffer()) )); } // Wipe keys DH1.wipe(); DH2.wipe(); DH3.wipe(); recipientX.wipe(); return { Sender: r.Sender, SK: SK, IK: senderIdentityKey };}/** * Initiate an X3DH handshake as a recipient * * @param {object} req - Request object * @returns {string} - The initial message */async function x3dh_initiate_recv(req) { const {identitySecret, identityPublic} = await getIdentityKeypair(); const {preKeySecret, preKeyPublic} = await getPreKeyPair(); const {Sender, SK, IK} = await x3dh_initiate_recv_get_sk( req, identitySecret, identityPublic, preKeySecret, preKeyPublic ); const assocData = await sodium.sodium_bin2hex( new Uint8Array( [].concat(IK.getBuffer()) .concat(identityPublic.getBuffer()) ) ); try { await setSessionKey(senderIdentity, SK); return decryptData( req.CipherText, await getEncryptionKey(senderIdentity), assocData ); } catch (e) { await destroySessionKey(senderIdentity); throw e; }}
And with that, you’ve successfully implemented X3DH and symmetric encryption in JavaScript.
We abstracted some of the details away (i.e. kdf()
, the transport mechanisms, the session key management mechanisms, and a few others). Some of them will be highly specific to your application, so it doesn’t make a ton of sense to flesh them out.
One thing to keep in mind: According to the X3DH specification, participants should regularly (e.g. weekly) replace their Signed Pre-Key in the server with a fresh one. They should also publish more One-Time Keys when they start to run low.
If you’d like to see a complete reference implementation of X3DH, as I mentioned before, Rawr-X3DH implements it in TypeScript.
Session Key Management
Using X3DH to for every message is inefficient and unnecessary. Even the Signal Protocol doesn’t do that.
Instead, Signal specifies a Double Ratchet protocol that combines a Symmetric-Key Ratchet on subsequent messages, and a Diffie-Hellman-based ratcheting protocol.
Signal even specifies integration guidelines for the Double Ratchet with X3DH.
It’s worth reading through the specification to understand their usages of Key-Derivation Functions (KDFs) and KDF Chains.
Although it is recommended to use HKDF as the Signal protocol specifies, you can strictly speaking use any secure keyed PRF to accomplish the same goal.
What follows is an example of a symmetric KDF chain that uses BLAKE2b with 512-bit digests of the current session key; the leftmost half of the BLAKE2b digest becomes the new session key, while the rightmost half becomes the encryption key.
const SESSION_KEYS = {};/** * Note: In reality you'll want to have two separate sessions: * One for receiving data, one for sending data. * * @param {string} identity * @param {CryptographyKey} key */async function setSessionKey(identity, key) { SESSION_KEYS[identity] = key;}async function getEncryptionKey(identity) { if (!SESSION_KEYS[identity]) { throw new Error("No session key for " + identity"); } const blake2bMac = await sodium.crypto_generichash( SESSION_KEYS[identity], null, 64 ); SESSION_KEYS[identity] = new CryptographyKey(blake2bMac.slice(0, 32)); return new CryptographyKey(blake2bMac.slice(32, 64));}
In the interest of time, a full DHRatchet implementation is left as an exercise to the reader (since it’s mostly a state machine), but using the appropriate functions provided by sodium-plus (crypto_box_keypair()
, crypto_scalarmult()
) should be relatively straightforward.
Make sure your KDFs use domain separation, as per the Signal Protocol specifications.
Group Key Agreement
The Signal Protocol specified X3DH and the Double Ratchet for securely encrypting information between two parties.
Group conversations are trickier, because you have to be able to encrypt information that multiple recipients can decrypt, add/remove participants to the conversation, etc.
(The same complexity comes with multi-device support for end-to-end encryption.)
The best design I’ve read to date for tackling group key agreement is the IETF Messaging Layer Security RFC draft.
I am not going to implement the entire MLS RFC in this blog post. If you want to support multiple devices or group conversations, you’ll want a complete MLS implementation to work with.
Brief Recap
That was a lot of ground to cover, but we’re not done yet.
(Art by Khia.)
So far we’ve tackled encryption, initial key agreement, and session key management. However, we did not flesh out how Identity Keys (which are signing keys–Ed25519 specifically–rather than Diffie-Hellman keys) are managed. That detail was just sorta hand-waved until now.
So let’s talk about that.
III. Identity Key Management
There’s a meme among technology bloggers to write a post titled “Falsehoods Programmers Believe About _____”.
Fortunately for us, Identity is one of the topics that furries are positioned to understand better than most (due to fursonas): Identities have a many-to-many relationship with Humans.
In an end-to-end encryption protocol, each identity will consist of some identifier (phone number, email address, username and server hostname, etc.) and an Ed25519 keypair (for which the public key will be published).
But how do you know whether or not a given public key is correct for a given identity?
This is where we segue into one of the hard problems in cryptography, where the solutions available are entirely dependent on your threat model: Public Key Infrastructure (PKI).
Some common PKI designs include:
- Certificate Authorities (CAs) — TLS does this
- Web-of-Trust (WoT) — The PGP ecosystem does this
- Trust On First Use (TOFU) — SSH does this
- Key Transparency / Certificate Transparency (CT) — TLS also does this for ensuring CA-issued certificates are auditable (although it was originally meant to replace Certificate Authorities)
And you can sort of choose-your-own-adventure on this one, depending on what’s most appropriate for the type of software you’re building and who your customers are.
One design I’m particularly fond of is called Gossamer, which is a PKI design without Certificate Authorities, originally designed for making WordPress’s automatic updates more secure (i.e. so every developer can sign their theme and plugin updates).
Since we only need to maintain an up-to-date repository of Ed25519 identity keys for each participant in our end-to-end encryption protocol, this makes Gossamer a suitable starting point.
Gossamer specifies a limited grammar of Actions that can be performed: AppendKey, RevokeKey, AppendUpdate, RevokeUpdate, and AttestUpdate. These actions are signed and published to an append-only cryptographic ledger.
I would propose a sixth action: AttestKey, so you can have WoT-like assurances and key-signing parties. (If nothing else, you should be able to attest that the identity keys of other cryptographic ledgers in the network are authentic at a point in time.)
IV. Backdoor Resistance
In the previous section, I proposed the use of Gossamer as a PKI for Identity Keys. This would provide Ed25519 keypairs for use with X3DH and the Double Ratchet, which would in turn provide session keys to use for symmetric authenticated encryption.
If you’ve implemented everything preceding this section, you have a full-stack end-to-end encryption protocol. But let’s make intelligence agencies and surveillance capitalists even more mad by making it impractical to backdoor our software (and impossible to silently backdoor it).
How do we pull that off?
You want Binary Transparency.
For us, the implementation is simple: Use Gossamer as it was originally intended (i.e. to secure your software distribution channels).
Gossamer provides up-to-date verification keys and a commitment to a cryptographic ledger of every software update. You can learn more about its inspiration here.
It isn’t enough to merely use Gossamer to manage keys and update signatures. You need independent third parties to use the AttestUpdate action to assert one or more of the following:
- That builds are reproducible from the source code.
- That they have reviewed the source code and found no evidence of backdoors or exploitable vulnerabilities.
(And then you should let your users decide which of these independent third parties they trust to vet software updates.)
Closing Remarks
The U.S. Government cries and moans a lot about “criminals going dark” and wonders a lot about how to solve the “going dark problem”.
If more software developers implement end-to-end encryption in their communications software, then maybe one day they won’t be able to use dragnet surveillance to spy on citizens and they’ll be forced to do actual detective work to solve actual crimes.
Y’know, like their job description actually entails?
Let’s normalize end-to-end encryption. Let’s normalize backdoor-resistant software distribution.
Let’s collectively tell the intelligence community in every sophisticated nation state the one word they don’t hear often enough:
Especially if you’re a furry. Because we improve everything! :3
Questions You Might Have
What About Private Contact Discovery?
That’s one of the major reasons why the thing we’re building isn’t meant to compete with Signal (and it MUST NOT be advertised as such):
Signal is a privacy tool, and their servers have no way of identifying who can contact who.
What we’ve built here isn’t a complete privacy solution, it’s only providing end-to-end encryption (and possibly making NSA employees cry at their desk).
Does This Design Work with Federation?
Yes. Each identifier string can be [username] at [hostname].
What About Network Metadata?
If you want anonymity, you want to use Tor.
Why Are You Using Ed25519 Keys for X3DH?
If you only read the key agreement section of this blog post and the fact that I’m passing around Ed25519 public keys seems weird, you might have missed the identity section of this blog post where I suggested piggybacking on another protocol called Gossamer to handle the distribution of Ed25519 public keys. (Gossamer is also beneficial for backdoor resistance in software update distribution, as described in the subsequent section.)
Furthermore, we’re actually using birationally equivalent X25519 keys derived from the Ed25519 keypair for the X3DH step. This is a deviation from what Signal does (using X25519 keys everywhere, then inventing an EdDSA variant to support their usage).
const publicKeyX = await sodium.crypto_sign_ed25519_pk_to_curve25519(foxPublicKey);const secretKeyX = await sodium.crypto_sign_ed25519_sk_to_curve25519(wolfSecretKey);
(Using fox/wolf instead of Alice/Bob, because it’s cuter.)
This design pattern has a few advantages:
- It makes Gossamer integration seamless, which means you can use Ed25519 for identities and still have a deniable X3DH handshake for 1:1 conversations while implementing the rest of the designs proposed.
- This approach to X3DH can be implemented entirely with libsodium functions, without forcing you to write your own cryptography implementations (i.e. for XEdDSA).
The only disadvantages I’m aware of are:
- It deviates from Signal’s core design in a subtle way that means you don’t get to claim the exact same advantages Signal does when it comes to peer review.
- Some cryptographers are distrustful of the use of birationally equivalent X25519 keys from Ed25519 keys (although there isn’t a vulnerability any of them have been able to point me to that doesn’t involve torsion groups–which libsodium’s implementation already avoids).
If these concerns are valid enough to decide against my implementation above, I invite you to talk with cryptographers about your concerns and then propose alternatives.
Has Any of This Been Implemented Already?
You can find implementations for the designs discussed on this blog post below:
- Rawr-X3DH implements X3DH in TypeScript (added 2020-11-23)
I will update this section of the blog post as implementations surface.
https://soatok.blog/2020/11/14/going-bark-a-furrys-guide-to-end-to-end-encryption/
#authenticatedEncryption #authenticatedKeyExchange #crypto #cryptography #encryption #endToEndEncryption #libsodium #OnlinePrivacy #privacy #SecurityGuidance #symmetricEncryption
Zoom recently announced that they were going to make end-to-end encryption available to all of their users–not just customers.https://twitter.com/zoom_us/status/1320760108343652352
This is a good move, especially for people living in countries with inept leadership that failed to address the COVID-19 pandemic and therefore need to conduct their work and schooling remotely through software like Zoom. I enthusiastically applaud them for making this change.
End-to-end encryption, on by default, is a huge win for everyone who uses Zoom. (Art by Khia.)
The end-to-end encryption capability arrives on the heels of their acquisition of Keybase in earlier this year. Hiring a team of security experts and cryptography engineers seems like a good move overall.
Upon hearing this news, I decided to be a good neighbor and take a look at their source code, with the reasoning, “If so many people’s privacy is going to be dependent on Zoom’s security, I might as well make sure they’re not doing something ridiculously bad.”
Except I couldn’t find their source code anywhere online. But they did publish a white paper on Github…
(Art by Khia.)
Disclaimers
What follows is the opinion of some guy on the Internet with a fursona–so whether or not you choose to take it seriously should be informed by this context. It is not the opinion of anyone’s employer, nor is it endorsed by Zoom, etc. Tell your lawyers to calm their nips.More importantly, I’m not here to hate on Zoom for doing a good thing, nor on the security experts that worked hard on making Zoom better for their users. The responsibility of security professionals is to the users, after all.
Also, these aren’t zero-days, so don’t try to lecture me about “responsible” disclosure. (That term is also problematic, by the way.)
Got it? Good. Let’s move on.
(Art by Swizz.)
Bizarre Design Choices in Version 2.3 of Zoom’s E2E White Paper
Note: I’ve altered the screenshots to be white text on a black background, since my blog’s color scheme is darker than a typical academic PDF. You can find the source here.Cryptographic Algorithms
It’s a little weird that they’re calculating a signature over SHA256(Context) || SHA256(M), considering Ed25519 uses SHA512 internally.
It would make just as much sense to sign Context || M directly–or, if pre-hashing large streams is needed, SHA512(Context || M).
At the top of this section, it says it uses libsodium’s
crypto_box
interface. But then they go onto… not actually use it.Instead, they wrote their own protocol using HKDF, two SHA256 hashes, and XChaCha20-Poly1305.
While secure, this isn’t really using the crypto_box interface.
The only part of the libsodium interface that’s being used is
[url=https://github.com/jedisct1/libsodium/blob/927dfe8e2eaa86160d3ba12a7e3258fbc322909c/src/libsodium/crypto_box/curve25519xsalsa20poly1305/box_curve25519xsalsa20poly1305.c#L35-L46]crypto_box_beforenm()[/url]
, which could easily have been a call tocrypto_scalarmult()
instead (since they’re passing the output of the scalar multiplication to HKDF anyway).(Art by Riley.)
Also, the SHA256(a) || SHA256(b) pattern returns. Zoom’s engineers must love SHA256 for some reason.
This time, it’s in the additional associated data for the XChaCha20-Poly1305.
Binding the ciphertext and the signature to the same context string is a sensible thing to do, it’s just the concatenation of SHA256 hashes is a bit weird when SHA512 exists.
Meeting Leader Security Code
Here we see Zoom using the a SHA256 of a constant string (“
Zoombase-1-ClientOnly-MAC-SecurityCode
“) in a construction that tries but fails to be HMAC.And then they concatenate it with the SHA256 hash of the public key (which is already a 256-bit value), and then they hash the whole thing again.
It’s redundant SHA256 all the way down. The redundancy of “MAC” and “SecurityCode” in their constant string is, at least, consistent with the rest of their design philosophy.
It would be a real shame if double-hashing carried the risk of invalidating security proofs, or if the security proof for HMAC required a high Hamming distance of padding constants and this design decision also later saved HMAC from related-key attacks.
Hiding Personal Details
Wait, you’re telling me Zoom was aware of HMAC’s existence this whole time?I give up!
Enough Pointless Dunking, What’s the Takeaway?
None of the design decisions Zoom made that I’ve criticized here are security vulnerabilities, but they do demonstrate an early lack of cryptography expertise in their product design.After all, the weirdness is almost entirely contained in section 3 of their white paper, which describes the “Phase I” of their rollout. So what I’ve pointed out here appears to be mostly legacy cruft that wasn’t risky enough to bother changing in their final design.
The rest of their paper is pretty straightforward and pleasant to read. Their design makes sense in general, and each phase includes an “Areas to Improve” section.
All in all, if you’re worried about the security of Zoom’s E2EE feature, the only thing they can really do better is to publish the source code (and link to it from the whitepaper repository for ease-of-discovery) for this feature so independent experts can publicly review it.
However, they seem to be getting a lot of mileage out of the experts on their payroll, so I wouldn’t count on that happening.
https://soatok.blog/2020/10/28/bizarre-design-choices-in-zooms-end-to-end-encryption/
#encryption #endToEndEncryption #zoom
Zoom recently announced that they were going to make end-to-end encryption available to all of their users–not just customers.
https://twitter.com/zoom_us/status/1320760108343652352
This is a good move, especially for people living in countries with inept leadership that failed to address the COVID-19 pandemic and therefore need to conduct their work and schooling remotely through software like Zoom. I enthusiastically applaud them for making this change.
End-to-end encryption, on by default, is a huge win for everyone who uses Zoom. (Art by Khia.)
The end-to-end encryption capability arrives on the heels of their acquisition of Keybase in earlier this year. Hiring a team of security experts and cryptography engineers seems like a good move overall.
Upon hearing this news, I decided to be a good neighbor and take a look at their source code, with the reasoning, “If so many people’s privacy is going to be dependent on Zoom’s security, I might as well make sure they’re not doing something ridiculously bad.”
Except I couldn’t find their source code anywhere online. But they did publish a white paper on Github…
(Art by Khia.)
Disclaimers
What follows is the opinion of some guy on the Internet with a fursona–so whether or not you choose to take it seriously should be informed by this context. It is not the opinion of anyone’s employer, nor is it endorsed by Zoom, etc. Tell your lawyers to calm their nips.
More importantly, I’m not here to hate on Zoom for doing a good thing, nor on the security experts that worked hard on making Zoom better for their users. The responsibility of security professionals is to the users, after all.
Also, these aren’t zero-days, so don’t try to lecture me about “responsible” disclosure. (That term is also problematic, by the way.)
Got it? Good. Let’s move on.
(Art by Swizz.)
Bizarre Design Choices in Version 2.3 of Zoom’s E2E White Paper
Note: I’ve altered the screenshots to be white text on a black background, since my blog’s color scheme is darker than a typical academic PDF. You can find the source here.
Cryptographic Algorithms
It’s a little weird that they’re calculating a signature over SHA256(Context) || SHA256(M), considering Ed25519 uses SHA512 internally.
It would make just as much sense to sign Context || M directly–or, if pre-hashing large streams is needed, SHA512(Context || M).
At the top of this section, it says it uses libsodium’s crypto_box
interface. But then they go onto… not actually use it.
Instead, they wrote their own protocol using HKDF, two SHA256 hashes, and XChaCha20-Poly1305.
While secure, this isn’t really using the crypto_box interface.
The only part of the libsodium interface that’s being used is [url=https://github.com/jedisct1/libsodium/blob/927dfe8e2eaa86160d3ba12a7e3258fbc322909c/src/libsodium/crypto_box/curve25519xsalsa20poly1305/box_curve25519xsalsa20poly1305.c#L35-L46]crypto_box_beforenm()[/url]
, which could easily have been a call to crypto_scalarmult()
instead (since they’re passing the output of the scalar multiplication to HKDF anyway).
(Art by Riley.)
Also, the SHA256(a) || SHA256(b) pattern returns. Zoom’s engineers must love SHA256 for some reason.
This time, it’s in the additional associated data for the XChaCha20-Poly1305.
Binding the ciphertext and the signature to the same context string is a sensible thing to do, it’s just the concatenation of SHA256 hashes is a bit weird when SHA512 exists.
Meeting Leader Security Code
Here we see Zoom using the a SHA256 of a constant string (“Zoombase-1-ClientOnly-MAC-SecurityCode
“) in a construction that tries but fails to be HMAC.
And then they concatenate it with the SHA256 hash of the public key (which is already a 256-bit value), and then they hash the whole thing again.
It’s redundant SHA256 all the way down. The redundancy of “MAC” and “SecurityCode” in their constant string is, at least, consistent with the rest of their design philosophy.
It would be a real shame if double-hashing carried the risk of invalidating security proofs, or if the security proof for HMAC required a high Hamming distance of padding constants and this design decision also later saved HMAC from related-key attacks.
Hiding Personal Details
Wait, you’re telling me Zoom was aware of HMAC’s existence this whole time?
I give up!
Enough Pointless Dunking, What’s the Takeaway?
None of the design decisions Zoom made that I’ve criticized here are security vulnerabilities, but they do demonstrate an early lack of cryptography expertise in their product design.
After all, the weirdness is almost entirely contained in section 3 of their white paper, which describes the “Phase I” of their rollout. So what I’ve pointed out here appears to be mostly legacy cruft that wasn’t risky enough to bother changing in their final design.
The rest of their paper is pretty straightforward and pleasant to read. Their design makes sense in general, and each phase includes an “Areas to Improve” section.
All in all, if you’re worried about the security of Zoom’s E2EE feature, the only thing they can really do better is to publish the source code (and link to it from the whitepaper repository for ease-of-discovery) for this feature so independent experts can publicly review it.
However, they seem to be getting a lot of mileage out of the experts on their payroll, so I wouldn’t count on that happening.
https://soatok.blog/2020/10/28/bizarre-design-choices-in-zooms-end-to-end-encryption/
#encryption #endToEndEncryption #zoom
As America prepares for record-breaking infection statistics on a daily basis, many of us are looking at other countries safely reopening and wondering, “Why can’t we have nice things?”What you see if you type “COVID-19 statistics” into a search engine. Data sourced from Wikipedia.
Of course, everyone has their favorite target to blame for this catastrophe. Democrats blame Republicans. Republicans blame Democrats.
I’m not interested in blame. Regardless of who takes the blame in the end, the responsibility for fixing this problem is shared among everyone. Instead, I’m more interested in answering the “Why?” question.
Why Did Things Get This Bad?
Art by circuitslime.There are a lot of popular theories–many of them politically useful–about why the COVID-19 crisis is particularly bad in the United States.
A Failure of Trump’s Leadership?
Let’s get this one out of the way:Was the current hellscape we found ourselves in a direct consequence of Donald J Trump’s failure to ethically and responsibly use his power as President of the United States in the best interest of the people?
https://www.youtube.com/embed/svrxYLvJYto?feature=oembed
“It’ll miraculously go away in April!” – Morons
It’s certain that Trump has totally failed at leadership, but I don’t think that’s a satisfactory explanation for the current crisis.https://www.youtube.com/embed/s9vzT-0hchw?feature=oembed
That is not to say that Trump is without fault! Just that the problem is bigger than one idiot in a three piece suit.
Challenges Due to Scale?
A lot of the countries that performed better at responding to COVID-19 had smaller populations and occupied smaller land masses than the United States. Is that a reasonable explanation for why the USA suffers?Per-capita analyses and samples from other countries with similar populations and occupied surface area would be consistent with the USA if that was the reason. This problem is mostly uniquely American.
Are the Protesters at Fault?
COVID-19 has an incubation period of up to two weeks.The first signs of an uptick in COVID-19 infections was visible early into the nationwide Black Lives Matter protests, which implicates an earlier cause. The most likely one was the Memorial Day weekend celebrations that took place before George Floyd’s murder sparked widespread outrage.
Indeed, a further analysis did not show an uptick of COVID-19 infections even 4 weeks into the nationwide protests (which is two incubation periods).
Instead, the sharp spike in COVID-19 infections–factoring in the incubation period–coincided with states reopening their bars and restaurants. (Especially Florida.)
Why Things Are So Bad Today
The problem that America faces is the same one we’ve been faced with for many decades: Rampant Anti-Intellectualism.https://www.youtube.com/embed/bZnBL2dFgyI?feature=oembed
American anti-intellectualism is the juxtaposition of proud ignorance and conspiracy theories.
Let me ask all you female mask wearing ASSHOLES… are you ready to put a burka on next?That mask is NOT about your safety…. it's about MIND CONTROL
The only reason I know masks are worthless is because Andrew Cuomo keeps telling EVERYONE to wear one
Stick it up your ass!
— 🇺🇸🍺TRUMP WON🍺🇺🇸 (@PISDI94_96) June 30, 2020
Tweet is also archived in case it gets deleted.
Anti-intellectualism takes many forms:Every single time y'all tell me you're not ready to submit a talk on a subject you've been researching for months, I want you to think about "I don't actually ride in Ubers" internet-commentator guy. pic.twitter.com/aK2LAcFtzb— Lesley Carhart (@hacks4pancakes) July 1, 2020
People are so willing to die on the hill of their ignorance that even literally dying doesn’t deter them from campaigning for self-destruction.
RIGHT NOW: Dozens are marching in Sanford chanting “My body. My choice.” They are protesting after a mask order went into place in Seminole County today. pic.twitter.com/kMT7EebDKN— Stephanie Buffamonte (@StephBuffamonte) July 1, 2020
The reason that things are so bad in the United States of America boils down to the following:
- Too many Americans are proud to be ignorant, and in many cases, argue in support of “my ignorance is just as good as your facts”.
- Too many Americans are susceptible to bullshit conspiracy theories.
- Too many Americans are so selfish and short-sighted that they’d rather go to bars and waste money they don’t have on alcohol and shallow conversation than save the lives of the people they profess to love and care about.
- Conservative politics and media is a death cult that literally turned “wearing a mask to stop COVID-19” into a culture war issue.
- The people I’ve described in points 1-4 vote in every election, to make sure someone representing their bullshit has a seat at the political table.
It’s far too tempting to scapegoat the sitting President–especially when they’re as terrible as Donald J Trump. But if you do that, you’re ignoring the reason that he’s in the oval office to begin with.
Willful Ignorance Kills
I’ve talked about this before, when I used to write on Medium:
- https://medium.com/@soatok/american-ignorance-in-2020-c72c78d11dbb
- https://medium.com/@soatok/dear-furries-bullshit-and-misinformation-will-hurt-you-4a6f531d62bd
The sole cause for the situation we’re in is the same anti-intellectualism that Isaac Asimov complained about back in 1980.
Even if you want to solely blame Donald Trump, about 40% of Americans currently approve of his presidency (archive).
How to Escape This Hellscape
Art by Swizz.The only way to get out of the mess we’re in today is to stop tolerating ignorance and bullshit in your daily life. (Yes, this means you too, furry fandom! It’s not “all fun and games” anymore.)
That means, at a minimum:
- Not spreading the Myers-Brigg personality test bullshit
- Not giving the anti-LGBTQIA+ bigots at Chick-Fil-A any money
- Listening to experts (this means: SCIENTISTS, not talk show hosts or politicians)
- Being willing to admit “I don’t know” and then being curious enough to seek the truth
- Stop reading or financially supporting biased news media
Even if we manage to get out of the current COVID-19 hellscape without addressing these flaws, the next catastrophe will hit us just as hard.
Can People’s Minds Be Changed?
No. I don’t think most of the willfully ignorant assholes currently living in America that favor Trump’s presidency today are willing and capable of redemption.There will be exceptions, and we should remain open to the possibility of some people coming around, but in general most of these jerks will dig their heels in when pressured.
Instead, we’re going to have to wait for them to die off naturally.
What we can do in the meantime is promote better education for the American kids.
https://www.youtube.com/embed/ILQepXUhJ98?feature=oembed
A nation of enlightened free-thinkers fully capable of critical thought would be a good thing (even if Carlin thinks it will never happen). And we can get there, eventually.
All it takes is everyone deciding to be humble and actually verify what other people tell them (n.b. by referencing reputable sources).
It might not make a difference today, but in 10 or 20 years, a consistent effort to enable younger Americans to become smarter, wiser, and more empathetic than their parents and grandparents will change the political landscape of our country–and maybe even the world–for the better.
Art by Khia
Of course, the Powers That Be know that, which is why we see bullshit like this keep happening during a pandemic:
With a stroke of his veto pen, Gov. Ron DeSantis wiped out the entire $29.4 million budget for a suite of online education services that have become critical to students and faculty during the Covid-19 outbreak https://t.co/6PMop4SIPv— POLITICO (@politico) June 30, 2020
Remember, DeSantis is the governor of the state whose COVID-19 infections-per-day graph looks like this:
You can see a clear data pattern with Florida's COVID-19 with a lull each Sunday. I've computed the baseline for this week (Sunday's numbers) and the last two weeks' increase relative to Sunday. We're easily on track to hit 10,000 new cases Friday-ish, maybe even higher. pic.twitter.com/8pnXF5uEwR— 💙💛 "Dog Boy" Nex' 💙💛 (@NexJql) July 1, 2020
It won’t be easy. Bullshit is everywhere. But it’s doable.
Addendum: A Carnival of Stupid
In case you still had any doubt about the potent lethality of American anti-intellectualism, look no further than this story:Florida teen dies after conspiracy theorist mom takes her to church ‘COVID party’ and tries to treat her with Trump-approved drug: report – https://t.co/Bw3SMVitxx— Jeffrey Levin 🇺🇦 (@jilevin) July 6, 2020
We have to demand better of ourselves before we can demand better of others. But damn if the bar isn’t really, really low to begin with.
I believe someday we'll open up textbooks and find this screenshot under the definition of "cognitive dissonance". pic.twitter.com/n535Obq6SB— 🦊 Ennex is trying this again! 🦊 (@EnnexTheFox) July 7, 2020
The White House Press Secretary on Trump's push to reopen schools: "The science should not stand in the way of this."
— Jim Acosta (@Acosta) July 16, 2020
https://soatok.blog/2020/07/02/how-and-why-america-was-hit-so-hard-by-covid-19/
I have been a begrudging user of Telegram for years simply because that’s what all the other furries use, despite their cryptography being legendarily bad.
When I signed up, I held my nose and expressed my discontent at Telegram by selecting a username that’s a dig at MTProto’s inherent insecurity against chosen ciphertext attacks: IND_CCA3_Insecure
.
Art: CMYKat
I wrote about Furries and Telegram before, and included some basic privacy recommendations. As I said there: Telegram is not a private messenger. You shouldn’t think of it as one.
Recent Developments
Telegram and Elon Muck have recently begun attacking Signal and trying to paint it as insecure.
Matthew Green has a Twitter thread (lol) about it, but you can also read a copy here (archive 1, archive 2, PDF).
https://twitter.com/matthew_d_green/status/1789688236933062767
https://twitter.com/matthew_d_green/status/1789689315624169716
https://twitter.com/matthew_d_green/status/1789690652399170013
https://twitter.com/matthew_d_green/status/1789691417721282958
Et cetera.
This is shitty, and exacerbates a growing problem on Telegram: The prevalence of crypto-bros and fascist groups using it to organize.
Why Signal is Better for Furries
First, Signal has sticker packs now. If you want to use mine, here you go.
For years, the main draw for furries to Telegram over Signal was sticker packs. This is a solved problem.
Second, you can setup a username and keep your phone number private. You don’t need to give your phone number to strangers anymore!
(This used to be everyone’s criticism of Signal, but the introduction of usernames made it moot.)
Finally, it’s trivial for Americans to setup a second Signal account using Twilio or Google Voice, so you can compartmentalize your furry posting from the phone number your coworkers or family is likely to know.
(Note: I cannot speak to how to deal with technology outside of America, because I have never lived outside America for any significant length of time and do not know your laws. If this is relevant to you, ask someone in your country to help figure out how to navigate technological and political issues pertinent to your country; I am not local to you and have no fucking clue.)
The last two considerations were really what stopped furries (or queer people in general, really) from using Signal.
Why Signal?
There are two broadly-known private messaging apps that use state-of-the-art cryptography to ensure your messages are private, and one of them is owned by Meta (a.k.a., Facebook, which owns WhatsApp). So Signal is the only real option in my book.
That being said, Cwtch certainly looks like it may be promising in the near future. However, I have not studied its cryptography in depth yet. Neither has it been independently audited to my knowledge.
It’s worth pointing out that the lead developer of Cwtch is wrote a book titled Queer Privacy, so she’s overwhelmingly more likely to be receptive to the threat models faced by the furry community (which is overwhelmingly LGBTQ+).
For the sake of expedience, today, Signal is a “yes” and Cwtch is a hopeful “maybe”.
How I Setup a Second Signal Account
I own a Samsung S23, which means I can’t just use the vanilla Android tutorials for setting up a second profile on my device. Instead, I had to use the “Secure Folder” feature. The Freedom of the Press Foundation has more guidance worth considering.
If you don’t own a Samsung phone, you don’t need to bother with this “Secure Folder” feature (as the links above will tell you). You can just set up a work profile and get the same result! You probably also can’t access the same feature, since that’s a Samsung exclusive idiom. Don’t sweat it.
I don’t know anything about Apple products, so I can’t help you there, but there’s probably a way to set it up for yourself too. (If not, maybe consider this a good reason to stop giving abusive corporations like Apple money?)
The other piece of the puzzle you need is a second phone number. Google Voice is one way to acquire one; the other is to setup a Twilio account. There are plenty of guides online for doing that.
(Luckily, I’ve had one of these for several years, so I just used that.)
Why does Signal require a phone number?
The historical reason is that Signal was a replacement for text messaging (a.k.a., SMS). That’s probably still the official reason (though they don’t support SMS anymore).
From what I understand, the Signal development team has always been much more concerned about privacy for people that own mobile phones, but not computers, than they were concerned about the privacy of people that own computers, but not mobile phones.
After all, if you pick a random less privileged person, especially homeless or from a poor country, they’re overwhelmingly more likely to have a mobile phone than a computer. This doesn’t scratch the itch of people who would prefer to use PGP, but it does prioritize the least privileged people’s use case.
Their workflow, therefore, optimized for people that own a phone number. And so, needing a phone number to sign up wasn’t ever a problem they worried about for the people they were most interested in protecting.
Fortunately, using Signal doesn’t immediately reveal your phone number to anyone you want to chat with, ever since they introduced usernames. You still need one to register.
Tell Your Friends
I understand that the network effect is real. But it’s high time furries jettisoned Telegram as a community.
Lazy edit of the “Friendship Ended” meme
Finally, Signal is developed and operated by a non-profit. You should consider donating to them so that we can bring private messaging to the masses.
Addendum (2024-05-15)
I’ve been asked by several people about my opinions on other platforms and protocols.
Specifically, Matrix. I do not trust the Matrix developers to develop or implement a secure protocol for private messaging.
I don’t have an informed opinion about Signal forks (Session, Molly, etc.). Generally, I don’t review cryptography software for FOSS maximalists with skewed threat models unless I’m being paid to do so, and that hasn’t happened yet.
https://soatok.blog/2024/05/14/its-time-for-furries-to-stop-using-telegram/
#endToEndEncryption #furries #FurryFandom #privacy #Signal #Telegram
Update (2024-05-14): It’s time for furries to move away from Telegram.
A question I often get–especially from cryptography experts:What is it with furries and Telegram?
https://twitter.com/Monochromemutt/status/1407005415099883527
No, they’re almost certainly not talking about that.
Most furries use Telegram to keep in touch with other members of our community. This leads many to wonder, “Why Telegram of all platforms?”
The answer is simple: Stickers.
(Art by Khia.)
Telegram was the first major chat platform that allowed custom sticker packs to be uploaded and used by its users. This led to the creation of a fuckton of sticker packs for peoples’ fursonas.
How many furry sticker packs are there? Well, my friend Nican started a project to collect and categorize them all. You can find their project online at bunnypa.ws.
https://twitter.com/Nican/status/1200229213627801600
As of this writing, there are over 230,000 stickers across over 7,300 sticker packs (including mine). It also supports inline search!
https://twitter.com/BunnyPawsBot/status/1345902008339898371
Additionally, there’s a very strong network effect at play: Furries are going to gravitate to platforms with a strong furry presence.
With that mystery out of the way, I’d like to share a few of my thoughts about Telegram as a platform and how to make it manageable.
Don’t Use Telegram As a Secure Messenger
Despite at least one practical attack against MTProto caused by its poor authentication, Telegram refuses to implement encryption that’s half as secure as the stuff I publish under my furry identity.Instead, they ran a vapid “contest” and point to that as evidence of their protocol’s security.
If you’re a cryptography nerd, then you probably already understand that IND-CCA2 security is necessary for confidential messaging. You’re probably cautious enough to not depend on Telegram’s MTProto for privacy.
If you’re not a cryptography nerd, then you probably don’t care about any of this jargon or what it means.
It doesn’t help that they had another vulnerability that a renowned cryptography expert described as “the most backdoor-looking bug I’ve ever seen”.
(Art by Khia.)
So let’s be clear:
Telegram is best treated as a message board or a mailing list.
Use it for public communications, knowing full well that the world can read what you have to say. So long as that’s your threat model, you aren’t likely to ever get burned by the Durov family’s ego.
For anything that you’re not comfortable with being broadcast all over the Internet, you should use something more secure. Signal is the current recommended choice until something better comes along.
(Cwtch looks very good, but it’s not ready yet.)
Enable Folders to Make Notifications Reasonable
Last year, Telegram rolled out the ability to collect conversations, groups, and chats into folders. Most furries don’t know about this feature, because it doesn’t enable itself by default.First, open the hamburger menu (on desktop) or click on your icon (on mobile), then click Settings.
Next, you’ll see an option for Folders.
You should see a button that says “Create New Folder”.
From here, you can include Chats or general types of Chats (All Groups, All Channels, All Personal Conversations) and then exclude specific entries.
Give it a name and press “Create”. After a bit of organizing, you might end up with a setup like this.
Now, here’s the cool thing (but sadly doesn’t exist on all clients–use Telegram Desktop on Windows and Linux if you want it).
Once you’re done setting up your folders, back out to the main interface on Desktop and right click one of the folders, then press “Mark As Read”.
Finally, an easy button to zero out your notifications. Serenity at last!
Inbox Zero on Telegram? Hell yes!
(Art by Khia.)Note: Doing this to the special Unread folder is congruent to pressing Shift + ESC on Slack. You’re welcome, Internet!
Make Yourself Undiscoverable
In the default configuration, if anyone has your phone number in their address book (n.b. queerphobic relatives) and they install Telegram, you’ll get a notification about them joining.As you can imagine, that’s a bit of a terrifying prospect for a lot of people. Fortunately, you can turn this off.
Under Settings > Privacy and Security > Phone Number, you can limit the discovery to your contacts (n.b. in your phone’s address book).
Turn Off Notifications for Pinned Messages
Under Settings > Notifications, you will find the appropriate checkbox under the Events heading.A lot of furry Telegram groups like to notify all users whenever they pin a message. These notifications will even override your normal preferences if you disabled notifications for that group.
Also, you’re probably going to want to disable notifications for every channel / group / rando with very few exceptions, or else Telegram will quickly get super annoying.
Increase the Interface Scale
The default font size for Telegram is tiny. This is bad for accessibility.Fortunately, you can make the font bigger. Open the Settings menu and scroll down past the first set of options.
Set the interface scale to at least 150%. It will require Telegram to re-launch itself to take effect.
Don’t Rely on Persistent Message History
This is just a cautionary footnote, especially if you’re dealing with someone with a reputation for gaslighting: The other participant in a conversation can, at any point in time, completely or selectively erase messages from your conversation history.However, this doesn’t delete any messages you’ve already forwarded–be it to your Saved Messages or to a private Channel.
Aside: This is why, when someone gets outed for being a terrible human being, the evidence is usually preserved as forwarded messages to a channel.
Although Telegram isn’t in the same league as Signal and WhatsApp, its user experience is good–especially if you’re a furry.
I hope with the tips I shared above, as well as resources like bunnypa.ws, the Furry Telegram experience will be greatly improved for everyone that reads my blog.
Addendum: Beware the Furry Telegram Group List
A few people have asked me, “Why don’t you tell folks about furry-telegram-groups.net and/or @furlistbot?”The main reason is that a lot of the most popular groups on that listing are either openly or secretly run by a toxic personality cult called Furry Valley that I implore everyone to avoid.
https://soatok.blog/2021/06/22/a-furrys-guide-to-telegram/
#chat #communication #furries #furry #FurryFandom #privacySettings #stickers #Technology
The French Detention: Why We're Watching the Telegram Situation Closely
#endtoendencryption #freespeech #security #privacy #electronicfrontierfoundation #eff #digitalrights #digitalprivacy
posted by pod_feeder_v2
The French Detention: Why We're Watching the Telegram Situation Closely
EFF is closely monitoring the situation in France in which Telegram’s CEO Pavel Durov was charged with having committed criminal offenses, most of them seemingly related to the operation of Telegram.Electronic Frontier Foundation
After two and a half years of rewrite, #Fractal 5 is finally out! Get the #GTK 4 #Rust #Matrix client from https://flathub.org/fr/apps/org.gnome.Fractal and enjoy new features such as #EndToEndEncryption, location sharing, or multi-account with Single-Sign On 🚀