A key after the #: how shared links keep the server out
A shared conversation is stored on our server, and we can’t open it. How that works, where the password comes in, and what a link can’t protect.
Sharing usually means handing your words to whoever stores the link. Undergrove’s shared links are built so that the server holds a copy it can’t read. The trick is old and simple, and it has edges worth knowing.
The part after the #
When you share a conversation, your browser makes a new random key, encrypts a copy with AES-GCM and uploads only the ciphertext. The key goes into the link after the #. Browsers don’t send that part of an address to the server when they load a page, so the key reaches the person you send the link to and never us.
We keep the ciphertext for 1, 7 or 30 days. We see its id, its size and when it was made and opened. The page that opens it fetches the ciphertext, reads the key from the address bar and decrypts it in the tab.
A password, too
A link can also need a password. Then the key that opens the copy comes from both: the password is stretched with PBKDF2-SHA256 at 600,000 iterations and combined with the link’s key through HKDF. Neither the link nor the password opens it alone, and the password is checked in the tab against the copy already there, so we never see it.
Open once, and opening times
A link can open once. The server deletes its copy as it’s first read and keeps only the time it was opened, so the link can say it was used rather than that it never existed. Before fetching, the page asks you to confirm, so a chat app’s link preview doesn’t use it up.
A link can also open on a date. Until then the server refuses to hand over the ciphertext. That is the server keeping its word, not cryptography: the copy is already stored, and its key is already in the link.
What a link can’t protect
- Anyone with the whole link can read the copy. Treat it like the conversation itself.
- Chat and email apps store the messages you send, links included, # and all.
- The page that decrypts the copy is code we serve. /verify compares that code with the build’s published manifest.
The privacy page lists what’s stored for each link, and the threat model who could see what.
More field notes
What an attestation proves, and what it doesn’t
Your browser checks Tinfoil’s enclave before it sends a word. What that check establishes, where it stops, and what can’t be checked in a browser yet.
What a relay that can’t read still sees
Our relay passes ciphertext it can’t open. That isn’t the same as seeing nothing. An inventory of what it does see, and why.