chiffer.nu lets you exchange end-to-end encrypted messages with anyone, without creating an account, giving up an email address, or trusting a company with your data.
Messages are encrypted in your browser before they leave your device. The server stores only ciphertext it cannot read.
Each message is encrypted with a unique key that is immediately discarded. Compromising your keys today reveals nothing about messages from yesterday.
Cryptographic keys are generated in your browser and stored locally. They never leave your device. You can optionally set a password to protect them at rest.
Share a rotating 6-digit code with someone to connect — no usernames, no QR codes, no app installs required on either side.
For the cryptographically curious.
All cryptography runs in the browser via the Web Crypto API. No third-party crypto libraries are used. P-256 (NIST curve) was chosen over Ed25519/X25519 for its universal browser support (Chrome 37+, Firefox 35+, Safari 11+).
| Purpose | Algorithm |
|---|---|
| Session signing | ECDSA P-256 / SHA-256 |
| Initial key agreement | X3DH (4× ECDH P-256) |
| Forward-secret messaging | Double Ratchet (DH + symmetric ratchet) |
| Ratchet chain KDF | HMAC-SHA256 |
| Root / chain key derivation | HKDF / SHA-256 |
| Message & profile encryption | AES-GCM 256-bit |
| Private key protection | PBKDF2 / SHA-256, 600 000 iterations → AES-GCM |
| Safety number | ECDH → HKDF → HMAC-SHA256 (RFC 4226 truncation) |
Each account holds two independent P-256 key pairs: one for signing (identity), one for
encryption (messaging). Private keys are exported as PKCS8, encrypted under an AES-GCM key
(KEK), and stored in localStorage.
By default the KEK is derived from an empty password, so accounts work without any
password — the keys are protected only by browser storage isolation. Setting a password
derives the KEK via PBKDF2 (600 000 iterations, SHA-256), making offline brute-force
attacks significantly harder. The KEK is never stored or transmitted.
Pre-keys (signed pre-key and one-time pre-keys used by X3DH), ratchet state, and message history are stored in an encrypted IndexedDB database. Every record is individually AES-GCM encrypted with the same KEK. Changing or removing a password re-encrypts the entire database in-browser — no data leaves the device during this operation.
Sessions use a challenge-response protocol. The server issues 1 200 bytes of random data,
the client signs it with ECDSA using the account's signing private key, and the server
verifies the signature against the stored public key. On success a session token is issued
and sent in the X-Session-Id header on subsequent requests.
The first message to a new contact uses X3DH (Extended Triple Diffie-Hellman) to establish a shared secret from four ECDH operations involving the sender's identity key, an ephemeral key, and the recipient's identity key, signed pre-key, and one-time pre-key. This provides mutual authentication and forward secrecy even before the first reply.
Subsequent messages use the Double Ratchet algorithm. A symmetric ratchet advances the chain key (via HMAC-SHA256) to derive a unique message key for every message. A DH ratchet step — triggered each time a new ratchet public key arrives from the other party — refreshes the root key, breaking the chain so that compromising one session key cannot retroactively expose older messages.
Your contact list is stored on the server as an encrypted blob using an ECIES-style scheme: an ephemeral ECDH key pair is generated per write, the shared secret is derived against your own encryption public key, and the result is AES-GCM encrypted. The ephemeral public key is stored alongside the ciphertext. Only your device (holding the private key) can decrypt it.
To detect a man-in-the-middle attack (e.g. a compromised server substituting public keys), each conversation displays a rotating 6-digit safety number derived from the ECDH shared secret and each party's account ID. The number is unique per direction — Alice and Bob each see a different code — so neither can simply copy their own code to pass verification.
Verification works by reading your code aloud to your contact over a trusted channel (phone call, in person). They type it in; if it matches, both sides hold genuine keys and there is no MitM. The verification notification is itself sent as an encrypted message through the normal message pipeline, so the server cannot forge it.
No system is perfect. These are the honest trade-offs in the current design.
While message content is encrypted, the server necessarily sees sender and recipient account IDs when routing each message — even though it cannot read what was said. Messages are held only in memory (not written to the database) and expire after 24 hours, limiting the exposure window. A server compromise or legal order during that window could expose the social graph. The encrypted contact list mitigates this partially (the server doesn't know your contact names or aliases), but the message routing metadata remains readable.
Key exchange happens through the server. A compromised server could substitute public keys during the initial connection, silently reading all subsequent messages. The safety number ceremony is designed to detect this, but it is opt-in and requires both parties to complete an out-of-band comparison. Until that step is done, unverified contacts are implicitly trusted.
Keys are stored encrypted in localStorage.
Any JavaScript executing on the same origin (including injected scripts from a cross-site
scripting vulnerability) could read the encrypted key blob. If an attacker can also
capture the password as it is typed, they can derive the private key. This is an inherent
limitation of browser-based cryptography — native apps with hardware-backed keystores
(Secure Enclave, TPM) provide stronger isolation.
There is no mechanism to synchronise keys across multiple devices. Using chiffer.nu on a second device requires restoring from the backup file, after which both devices share the same key pair and the same account identity. There is no way to add a second device without granting it the same cryptographic capabilities as the first.
By default, accounts require no password — private keys are stored in localStorage protected only by browser storage isolation. Any script running on the same origin,
anyone with physical access to the browser profile, or any process that can read the
browser's storage directory can extract the keys directly without any password to crack.
Setting a password in Security settings adds PBKDF2-based key stretching (600 000
iterations), which makes offline brute-force attacks significantly harder against the
stored blob or a downloaded backup file.
Even with a password, an attacker with interactive access to your running browser faces the same threat as without one — they can observe you authenticate, capture the derived key from memory, or wait for the session to be active. The password primarily protects against offline attacks on an extracted blob (stolen device, leaked backup file, database breach). For the highest assurance, set a strong password and store backup files securely offline.
The server necessarily sees the IP address of every client that connects and maintains a persistent SSE connection while the session is active. Connection timing, session duration, and message-sending frequency are all observable by the server operator. Using chiffer.nu over Tor or a trusted VPN would reduce this exposure.
When you purge a conversation, the purge notification is sent as an encrypted message to the other party. If they do not reconnect within 24 hours, the notification itself expires and is deleted along with all other messages — and their local message cache will not be cleared automatically. In that scenario the other party retains the conversation in memory until they close or reload their browser tab.
More broadly, the purge feature is primarily about removing stored ciphertext from the server. It is not, and cannot be, a mechanism for recalling information. Any message that has already been delivered and decrypted on the recipient's device is entirely outside the sender's control. The recipient may have taken screenshots, copied the text, forwarded it, or stored it in any number of ways — none of which the sender can influence or detect.
This is a fundamental property of all communication systems, not a specific shortcoming of chiffer.nu: once you have told someone something, you cannot un-tell them. The purge feature offers a best-effort cleanup of server-side remnants, but should never be relied upon as a guarantee that the other party no longer has access to the content.