Proton's crypto is not Transparent and not OPAQUE

FinnKuhn1 pts0 comments

Proton's crypto is not Transparent and not OPAQUE

my_watercolor_sketchbook

2026-06-23 14 min read URL copied to clipboard!

Proton's crypto is not Transparent and not OPAQUE<br>Proton pitches itself as a privacy-respecting alternative to Google and friends. Their app suite (Mail, Password Manager, VPN, etc.) uses cryptography such that, in their words:<br>Our end-to-end encryption and zero-access encryption mean that no one (not even Proton) has the technical means to access your data without your permission. […] At Proton, privacy isn't a promise, it's mathematically ensured.

If Proton plays by the rules, their protocols probably provide adequate protection and give them a credible basis to deny lawful access requests. But the moment that changes, or their servers are compromised (a "malicious server" threat model), there are several technical paths into user data. In this post, I show new attack paths I discovered and responsibly disclosed to Proton: a flaw in their Key Transparency implementation allows for man-in-the-middle (MITM) attacks, and their desktop auto-updater can install unsigned executables without asking for permission. Read my conclusion on why I still use Proton to host my email and VPN.<br>Key Transparency<br>When two Proton users communicate, say over email, Proton uses public-key cryptography. The hard part is making sure the sender gets the right public key of the recipient in the first place. Encrypt with the wrong one and a man-in-the-middle reads the message instead of the intended recipient. Every end-to-end encrypted protocol has to solve this. On the web, browsers and operating systems ship an extensive list of certificate authorities, that serve as a root of "trust". End-to-end encrypted messengers like Signal show you a fingerprint and ask you to compare it over a separate channel, which works but only if users actually do it (they usually don't).<br>That's why Proton complemented their Signal-like manual address verification with Key Transparency (KT):<br>Depending on your threat model, you might want to make sure you're emailing the person you intend to by verifying their public key. […] Proton Mail's new key verification feature automatically checks your contacts' public keys, so you can be sure you're emailing the people you intend.

Proton's design is described in their whitepaper, based on a master's thesis by Thore Göbel supervised by ETH Zürich's Applied Crypto Group (you may know some of their greatest hits like zkae.io or breakingthe3ma.app). The protocol on paper is probably fine, the bug I found is in how it was implemented.<br>Proton maintains a list of all public keys across their apps, and a trusted external auditor could confirm all users see the same list every epoch (though I'm not aware of any external entities that currently do this). On top of that, clients regularly "self-audit" their own email address, verifying Proton lists the correct public key for them. The application does this non-interactively on startup, so it's more user-friendly than manual fingerprint comparison.<br>$ whoami<br>The KT log stores a Signed Key List (SKL) for each user, indexed by email address. The self-audit is supposed to confirm that the SKL at your address really contains your public keys. The problem is that the email address used in the self-audit is whatever the server says it is. The client fetches it fresh from the (untrusted) backend every time. Remember the threat model treats Proton's servers as untrusted, so the moment users audit the wrong address, the protocol's guarantees collapse.<br>To Proton's credit, their client implementations are open source, so we can trace the whole thing. Say my email is pascal@proton.me. My client runs a self-audit, and to figure out which address to verify, it politely asks the server via addressThunk. Proton could return the wrong address, say malicious@proton.me, and my client takes it at face value. This address is then used to verify my public key in the KT log with verifyProofs.ts.<br>Now Proton just needs to arrange the log accordingly. They place my genuine SKL (with my real public keys) at the position for malicious@proton.me, and a forged SKL containing the attacker's key at the position for pascal@proton.me. My self-audit looks up malicious@proton.me, finds my real keys, and passes happily in the background. Meanwhile Julia emails me, her client looks up pascal@proton.me, gets the attacker's key, encrypts to it, and Proton decrypts, reads, and re-encrypts to my real key. The flow looks like this:

Because the client trusts the server to tell it its own address, the self-audit verifies the wrong row, letting Proton serve a forged key for the real address.

An adversary could hide this attack further by using a malicious address that looks more similar to the real one (like pаscаl@proton.me with a cyrillic 'а') to fool even users who regularly check which addresses were audited. Or even simpler, the adversary could also return an empty list of audit...

proton address public audit email self

Related Articles