SpecificationVersion 1
How secretshare protects a secret.
This page describes exactly what the code does. The encryption runs in your browser. Our own code is served as plain, unminified JavaScript modules that you can read from the page itself; the two cryptographic libraries, @noble/post-quantum and hash-wasm, are their published builds. If this page and the code ever disagree, the code is the truth, and that's a bug we want to hear about.
The threat we design for
Assume our server is compromised, or that someone with access to it is curious. That person must not be able to read your secrets, now or later. They will see that a secret exists, its approximate size, when it was created and read. They will not see what it says, who sent it, or who it was for.
What we cannot protect against: a compromised device on either end, someone who obtains the full link, or a malicious change to the code we serve you. That last risk is why the code is kept small, readable, and loads nothing from third parties.
Sharing a secret
1 · Keys
Your browser draws three random values with crypto.getRandomValues: an identifier (128 bits), a link key K (256 bits) and a tracking token M (256 bits).
IKM = K (no passphrase) IKM = K ‖ Argon2id(passphrase) (with a passphrase) salt = "secretshare/v1/secret/" + id encKey = HKDF-SHA256(IKM, salt, info = "enc") → 256-bit AES key authKey = HKDF-SHA256(IKM, salt, info = "auth") → 256-bit access proof
2 · Encryption
The text and files are packed together, then padded to 4 KB, or to the next 64 KB step for larger content, so the size of the ciphertext does not reveal the length of a password. The result is encrypted with AES-256-GCM through the browser's Web Crypto API, with a fresh 96-bit IV and the salt above as additional authenticated data, which binds the ciphertext to its identifier.
ciphertext = 0x01 ‖ IV ‖ AES-256-GCM(encKey, IV, padded payload, AAD = salt)
3 · What is sent to us
The identifier, the ciphertext, SHA-256(authKey), SHA-256(M), the expiry and the number of views. Never K, never M, never the passphrase, never the plaintext.
4 · The link
https://secretshare.co/s/<id>#<K>
K lives in the fragment, the part after #. Browsers do not send the fragment to the server: it never appears in our requests, and we do not keep access logs in any case.
5 · Opening it
Loading the page only asks whether the secret still exists. Nothing is consumed until someone presses and holds Reveal for about a second. Link previews in Slack or Teams never run the page; some email security scanners do run it and click buttons, but a click alone does nothing here. After the hold, the browser derives authKey and sends it. The server compares its SHA-256 with the stored hash. Only on a match does it return the ciphertext, and, on the last allowed view, it deletes the ciphertext in the same database transaction. The browser then decrypts locally and removes K from the address bar.
This access proof means that an identifier alone is worthless: without K, you don't even get the encrypted bytes, so they cannot be attacked offline.
6 · Passphrases
An optional passphrase is stretched with Argon2id: 64 MiB of memory, 3 passes, parallelism 1, a random 16-byte salt (RFC 9106, second recommended setting), computed in your browser. Its output enters the key derivation above, so the right passphrase is needed both to obtain the ciphertext and to decrypt it. Each wrong attempt is counted on the server; the tenth wrong attempt destroys the secret.
7 · Tracking without an account
The tracking link /m/<id>#<M> proves knowledge of M to see read times or burn the secret. It cannot decrypt anything: M and K are independent.
Requesting a secret: post-quantum
A request reverses the direction: someone sends a secret to you. That needs public-key cryptography, and that is where quantum computers matter: data recorded today could be decrypted once large quantum computers exist. Requests therefore use X-Wing, a hybrid key encapsulation that combines ML-KEM-768 (NIST FIPS 203) with X25519. It stays secure as long as either one holds.
(pk, sk) = X-Wing.keygen() sk: 32 bytes, stays with you R = 32 random bytes note = AES-256-GCM(HKDF(R, "note"), your note, AAD = label ‖ SHA-256(pk)) request = https://secretshare.co/r/<id>#<R> → send it inbox = https://secretshare.co/i/<id>#<sk> → keep it (ct, ss) = X-Wing.encapsulate(pk) on their side answer = AES-256-GCM(HKDF(ss, "enc"), payload) ss = X-Wing.decapsulate(ct, sk) on your side
Because the note's authenticated data includes the hash of the public key, a server that swapped in its own public key would make the note impossible to decrypt, and the page refuses to send anything. The answer is returned to the inbox once, then erased.
Secret links themselves involve no public-key exchange: AES-256 with a 256-bit key that never travels over the network is already considered safe against quantum attacks.
The X-Wing implementation is @noble/post-quantum by Paul Miller (MIT license). It has been self-audited by its author, not yet by an independent firm.
What our servers keep
| Kept | Ciphertext (until read or expired), the two SHA-256 hashes, the Argon2id salt and settings, size, creation and expiry dates, read timestamps. |
|---|---|
| Never kept | Plaintext, keys, passphrases, IP addresses, cookies, access logs. |
| Deleted | Ciphertext: at the last allowed view, or when burned. The whole record: at its expiry date, checked every five minutes. |
| Abuse limits | Requests are counted per IP address for up to ten minutes, under a keyed hash (HMAC) of the address, never the address itself. |
Browser hardening
- A strict Content Security Policy: scripts only from our domain, each inline script authorized by a per-request nonce, no connections to other origins.
- No third-party scripts, fonts or analytics on any page. Visit counts go through our own server and contain only a page template, like
/s, never an identifier or a key. Referrer-Policy: no-referrer, framing forbidden, cross-origin isolation headers, HTTPS only.- Hosted on Kiora's own servers in France, behind TLS that negotiates the post-quantum X25519MLKEM768 key exchange with modern browsers.
Report a vulnerability
Write to contact@secretshare.co. We answer every report and credit researchers who wish to be named.