Yeetline

Our own security review

Internal review, not independent. This page is Yeetline's own account of attacking its own cryptography. It was done by the people who build it, nobody outside the project has checked it, and it is not an audit and does not replace one. It compares Yeetline with no other product. The project has no budget for an independent review today, and everything below is kept in the form a paid reviewer could start from when there is one.

It was done on 17 October 2026, on the code of the release being built that day. The full findings log, with every detail and the exact commits, is SECURITY-REVIEW-INTERNAL.md in the server's source repository. That repository is private today, so the log is not open to everyone: this page is the public version of it, and a reviewer or a customer's security team can ask for the log through the contact on the reporting page.

What was looked at

The cryptography that opens a message or signs in an account, and the code that decides which keys it is used with: the handshake and the ratchets, the sealed block, the account keystore, the sign-in hand-off between pages, the optional recovery key, and how a release is signed and checked.

In the findings log each part is marked with the one word that is true of it. Read means a person read the code looking for a way to break it. Attacked means an attack was written and run against the real code. Rerun means a check that already existed was run again. Some parts were only read: the number-theory and hash internals of the post-quantum code, the signing tool beyond its header, and the phone and desktop shells beyond their bundle check. Some were not looked at at all: most of the server's access rules, billing, the hosting scripts, the call relay and media, the search engine, and the third-party libraries the app ships.

What it found

IdWhatHow badStatus
IR-03The optional recovery key's backup was sealed to whatever public key the server named, so a server could be handed an account's identity keys, its self key and its personal key without the account having made a recovery keyCritical, in a feature not yet releasedFixed
IR-01A first message to a new conversation carried the sender's identity keys in a header that nothing compared with the sender's published keys, so a server could write a stranger's message as a contact's, past a verified safety numberHighFixed
IR-02Nothing a sender sealed said which conversation a message was for, so a server could move an unopened message into another conversationLowFixed for the app; one cost is an open defect (an administrator's console rename leaves unopened direct messages undecryptable)
L-05A message whose body was damaged on the way, or forged beside the real key wrap, used up the real message's key, so the real one, delivered next, could not be openedLowFixed

Each fix has its own commit and a check that runs the attack. Finding the first three took one kind of reading question: "who chose this value, who checked it, and are the check and the use looking at the same thing?" Each was a place where two parts that were each right met. The fourth was found by asking what had already changed when a check failed: the message's key was used up between the two checks a message has, one on its key and one on its body, so a failure of the second cost the person the real message. A message is now opened completely, key and body, before the device keeps anything, and a message that could not be opened yet stays in the thread as a placeholder that the real one replaces when it arrives.

What it tried that held

Fourteen attacks, each a check you can run: a bit changed anywhere in a message, its header or its key wrap; a message delivered twice; one message's body with another's key; a rewritten session id; a header that claims a thousand skipped messages; the post-quantum part removed from a first message or from a key bundle; identity keys from two accounts glued together; a sealed block changed anywhere or opened with the wrong key; the post-quantum code given a key or ciphertext it should refuse; a sign-in carried to another account, page or host; a bad point in place of a ratchet key; and polls built to damage the page that counts them.

What it found and did not fix

These are written down because a review that finds nothing is not credible, and one that hides what it did not fix is worse.

Sentences we corrected

Four sentences in the project's SECURITY.md were stronger than the code, and now say what the code does, and a second reading of these pages corrected four sentences on them that said more than the code or the facts allow. They are listed in the findings log.

Run it yourself

You need a checkout of the server's source repository (it carries a copy of the client in a folder of its own), node, python3 and its cryptography package; no server and no network. The repository is private today, so this is for a reviewer or a security team that has been given the source.

bash tools/review-evidence.sh

That runs the commands below one after another and prints the end of each one's output, plus the checks of the release path. Or run any of them yourself from the client's repository, or from the client folder of the server's:

python3 tests/test_interop.py        # the browser code against its Python second implementation
node tests/test_mlkem.mjs            # the post-quantum code against NIST's published vectors
node tests/test_scrypt.mjs           # the passphrase stretching against RFC 7914
node tests/test_review.mjs           # one check for every defect that was fixed
node tests/test_review_attacks.mjs   # the attacks that held, and the limits

What each is worth, said plainly. The post-quantum code and the stretching are checked against outside published answers (NIST and an RFC). Everything else is two implementations written by the same hand agreeing, which shows they are consistent and not that they are right. One formal model of the sign-in hand-off was written for a reviewer and run once, by us, with ProVerif 2.05 on 17 October 2026: it answered all nine of its queries as the design relies on, for the protocol as it stood then (the sign-in track's later changes are not modeled), which is a statement about the protocol under the model's assumptions and not about the code, and is not an outside review. A comparison of the post-quantum code with a third implementation exists but was skipped on the machine this was done on.

Handing it to someone who is paid to do this

The findings log lists every file with its size, how it was looked at, every finding with its rating and the commit that fixed it, every limit with what closing it would take, the claims that were corrected, the vectors and where they came from, and where we would look first: the whole handshake and ratchet composition against a formal model; the pieces that are new compositions of ordinary parts; who decides who is in a room; and the signed bundle that protects the phone and desktop apps.

If you find something we missed, tell us.

Last changed 2026-10-19