Client-Side Encryption: Your Browser Is the Vault.
The Trust Problem
If the server encrypts your messages, you have to trust the server. You have to trust that it uses the algorithm it claims. That it doesn't log plaintext before encrypting. That it doesn't keep a copy of the key. That its employees can't access your data. That it won't be compelled to add a backdoor.
That's a lot of trust. And if the server is compromised—by a breach, a rogue employee, or a legal order—your messages are compromised with it. Server-side encryption means the server is the single point of failure.
Moving Encryption to the Client
sTELgano uses the Web Crypto API to perform all encryption and decryption in your browser. The server never sees plaintext. It never sees your encryption key. It never sees your phone number or PIN.
The server is a blind relay. It stores ciphertext and forwards it to the other party. It cannot read what it stores. It cannot decrypt what it relays. Even under subpoena, the data it holds is cryptographically useless.
The Web Crypto API
The Web Crypto API is built into every modern browser. It's not a library you download—it's part of the browser itself, maintained by the browser vendors and audited by security researchers worldwide.
No external libraries needed. No npm packages for cryptography. No supply chain attacks through compromised dependencies. The browser provides everything.
Key Derivation
Your encryption key is derived from the steg number and room ID using PBKDF2 with 600,000 iterations. This is the OWASP 2023 recommendation for PBKDF2-HMAC-SHA256.
This happens entirely in your browser. The derivation takes approximately 2 seconds on a modern device—a minor inconvenience for you, but a massive barrier for an attacker trying to brute-force keys.
Each attempt to guess a key requires 600,000 hash operations. At scale, brute-force becomes computationally infeasible.
The derived key is a 256-bit AES key. It is never transmitted over the network. It is never stored on disk. It exists only in browser memory for the duration of the session, and is discarded when you close the tab.
Why the PIN Isn't Part of the Key
This is a design decision that often surprises people. If the PIN were part of the encryption key, each user would derive a different key—because they have different PINs. They wouldn't be able to decrypt each other's messages.
Both parties need the same encryption key to communicate. Since they share the same steg number but have different PINs, the PIN must be excluded from key derivation.
Instead, the PIN is used for access control. It's hashed into the access_hash, which proves you have the right to join the room. Think of it as the lock on the door—the encryption key is what's inside the vault.
The Encryption Flow
Plaintext
You type a message in the chat input. It exists only in browser memory.
Web Crypto API
The browser's built-in AES-256-GCM encrypt function is called with your derived key and a random 96-bit IV.
Ciphertext + IV
The encrypted output and the IV are base64-encoded. The plaintext is discarded from memory.
WebSocket Transit
The base64-encoded ciphertext and IV are sent over the WebSocket connection (already TLS-encrypted in transit).
Server Storage
The server stores the ciphertext and IV. It cannot decrypt them. It doesn't have the key.
Recipient Decryption
The recipient's browser receives the ciphertext, decrypts it with the same derived key, and displays the plaintext.
What the Server Sees
| Data | What the Server Has | Useful? |
|---|---|---|
| Phone Number | SHA-256 hash only | No |
| PIN | SHA-256 hash (in access_hash) | No |
| Messages | AES-256-GCM ciphertext | No |
| Encryption Key | Nothing | N/A |
| Sender Identity | SHA-256 hash only | No |
| IVs | 96-bit random nonces | No |
Even under subpoena, a full database dump reveals hashes and ciphertext. Without the client-side key—which was never transmitted and doesn't exist on the server—the data is cryptographically inert.
Zero External Dependencies
The entire cryptographic module is a single JavaScript file with no imports. No CryptoJS. No tweetnacl. No libsodium-wrappers. Just the Web Crypto API that ships with your browser.
Every external dependency is an attack vector. A compromised npm package, a malicious update, a supply chain injection—any of these could silently exfiltrate keys or plaintext. By depending on zero external cryptographic libraries, we eliminate this entire class of attack.
assets/js/crypto/anon.js
Single file. Zero imports. Complete crypto module.