WhisperBox is an end-to-end encrypted messenger that runs the entire cipher in the browser. RSA-OAEP wraps each conversation’s symmetric AES-GCM key, the user’s private key is itself wrapped behind PBKDF2 with 100,000 iterations, and the server only ever sees opaque envelopes it routes over WebSocket.
Crypto stays on the client
Registration generates a 2048-bit RSA-OAEP keypair and derives a wrapping key from the user’s password via PBKDF2-SHA256. The private key is encrypted with AES-GCM under that wrapping key, then handed to the server as an opaque blob. Sending a message generates a fresh AES-GCM key, encrypts the body, and wraps the symmetric key once for the recipient and once for the sender so history stays readable on either side.
The trade-offs I made on purpose
The README ships the limitations on the front page: JWTs live in localStorage, the unwrapped private key sits in IndexedDB during a session, PBKDF2 was the right call for a stage submission but Argon2 would be the right call for a product, and identity verification is trust-on-first-use. WhisperBox is a working proof — not a Signal replacement, and I wrote it that way.