Security model
What Encra protects against, what it doesn't, and why — with no marketing language.
Threat model
Encra is designed to protect message content even when the server infrastructure is fully compromised. The threat model assumes:
What we protect against
Server compromise
Server only stores public keys and ciphertext blobs. No plaintext, no private keys, no shared secrets. Even full server access yields nothing readable.
Network interception (MITM)
XSalsa20-Poly1305 is an authenticated cipher. Any modification to the ciphertext causes decryption to fail with DecryptionFailedError. The attacker cannot modify messages silently.
Key compromise revealing past messages
Message keys are deleted immediately after use (forward secrecy). An attacker who obtains today's ratchet state cannot decrypt past messages.
Key compromise revealing future messages
The DH ratchet generates new key material on every direction change. After the next DH step, the attacker loses access — they cannot decrypt future messages.
Weak randomness
All nonces and key pairs are generated by libsodium's randombytes_buf(), which uses the OS CSPRNG (getrandom on Linux, SecRandomCopyBytes on Apple).
Ciphertext malleability
Poly1305 MAC is computed over the entire ciphertext. Any bit flip is detected. The cipher is not malleable.
Limitations
⚠ These are real limitations, not disclaimers
⚠ Compromised endpoint
If Alice's device has malware, the attacker reads plaintext before encryption and after decryption. End-to-end encryption cannot protect against a compromised endpoint — this is true of Signal, WhatsApp, and every other E2E system.
⚠ Metadata analysis
The server can see who is talking to whom, when, and how frequently — just not what they're saying. If you need metadata protection, you need an onion routing layer (Tor) in addition to Encra.
⚠ No key verification by default
Without comparing safety numbers out-of-band, a compromised or malicious server could substitute keys (man-in-the-middle). Use generateFingerprint() and verify with your peer through a separate channel.
⚠ In-memory key storage
By default, private keys are held in memory for the session duration. Page reload = new key pair. For persistent sessions, keys should be stored in IndexedDB with device-level encryption.
Cryptographic primitives
| Purpose | Algorithm | Library | Security level |
|---|---|---|---|
| Key generation | X25519 keypair | libsodium crypto_box_keypair | 128-bit |
| Key exchange | X25519 ECDH | libsodium crypto_scalarmult | 128-bit |
| Message encryption | XSalsa20-Poly1305 | libsodium crypto_secretbox | 256-bit key |
| KDF (ratchet) | Keyed BLAKE2b-256 | libsodium crypto_generichash | 256-bit |
| Nonce generation | CSPRNG | libsodium randombytes_buf | 256-bit entropy |
| Safety numbers | BLAKE2b-256 | libsodium crypto_generichash | 256-bit |
ℹ Why libsodium?
Security red lines
These are hard rules in our codebase. We never cross them: