|
| 1 | +# WhatSaS — Web Client |
| 2 | + |
| 3 | +A secure end-to-end encrypted messaging web client built with vanilla JavaScript and the Web Crypto API. All encryption and decryption happens entirely in the browser — the server only ever sees ciphertext. |
| 4 | + |
| 5 | +--- |
| 6 | + |
| 7 | +## Prerequisites |
| 8 | + |
| 9 | +Before setting up the web client, ensure you have: |
| 10 | + |
| 11 | +- **Node.js** v18 or later — [download here](https://nodejs.org) |
| 12 | +- **npm** (comes with Node.js) |
| 13 | +- The **WhatSaS Flask server** running (see `server/README.md`) |
| 14 | +- A modern browser (Chrome 120+, Firefox 121+, Edge 120+) |
| 15 | + |
| 16 | +--- |
| 17 | + |
| 18 | +## Installation |
| 19 | + |
| 20 | +### Step 1 — Install dependencies |
| 21 | + |
| 22 | +Open a terminal, navigate to the `client-web` directory, and run: |
| 23 | + |
| 24 | +```bash |
| 25 | +cd client-web |
| 26 | +npm install |
| 27 | +``` |
| 28 | + |
| 29 | +This installs `argon2-browser` (used for Argon2id password hashing) into `node_modules/`. No build or compile step is needed — the client uses ES modules loaded directly by the browser. |
| 30 | + |
| 31 | +### Step 2 — Add background image (optional) |
| 32 | + |
| 33 | +The chat background uses `bows_background.png`. If you have this file, place it at: |
| 34 | + |
| 35 | +```text |
| 36 | +client-web/images/bows_background.png |
| 37 | +``` |
| 38 | + |
| 39 | +The app works without it — the background falls back to a plain pink gradient. |
| 40 | + |
| 41 | +### Step 3 — Start the backend server |
| 42 | + |
| 43 | +The web client has no standalone server of its own. It is served as static files by the Flask backend. Start the server from the project root: |
| 44 | + |
| 45 | +```bash |
| 46 | +cd server |
| 47 | +python run.py |
| 48 | +``` |
| 49 | + |
| 50 | +See `server/README.md` for full server setup instructions including environment variables and database setup. |
| 51 | + |
| 52 | +--- |
| 53 | + |
| 54 | +## Running the App |
| 55 | + |
| 56 | +Once the Flask server is running, open your browser and navigate to: |
| 57 | + |
| 58 | +```text |
| 59 | +https://sas.theburkenator.com |
| 60 | +``` |
| 61 | + |
| 62 | +For **local development** (Flask with SSL context on port 5000): |
| 63 | + |
| 64 | +```text |
| 65 | +https://localhost:5000 |
| 66 | +``` |
| 67 | + |
| 68 | +That's it — no separate build or bundling step required. |
| 69 | + |
| 70 | +--- |
| 71 | + |
| 72 | +## Project Structure |
| 73 | + |
| 74 | +```text |
| 75 | +client-web/ |
| 76 | +├── index.html # App shell — all views rendered dynamically by JS |
| 77 | +├── package.json # npm dependencies |
| 78 | +├── node_modules/ # Installed by npm install |
| 79 | +│ └── argon2-browser/ |
| 80 | +├── css/ |
| 81 | +│ ├── base.css # Reset, CSS variables, navbar, layout, buttons, forms |
| 82 | +│ ├── auth.css # Login, register, unlock views + password rule checklist |
| 83 | +│ ├── chat.css # Two-column chat layout, message bubbles, send bar |
| 84 | +│ └── verify.css # Blockchain verification view |
| 85 | +├── js/ |
| 86 | +│ ├── app.js # Router and navbar — controls which view is shown |
| 87 | +│ ├── api.js # All HTTP calls to the backend (fetch wrapper + session state) |
| 88 | +│ └── views/ |
| 89 | +│ ├── auth.js # Login, register, unlock screens |
| 90 | +│ ├── inbox.js # Chat threads, send, forward, revoke, delete |
| 91 | +│ └── helpers.js # Shared utilities (escaping, crypto helpers, TOFU, validation) |
| 92 | +├── crypto/ |
| 93 | +│ ├── constants.js # HKDF domain separation info strings |
| 94 | +│ ├── hkdf.js # HKDF-Extract and HKDF-Expand (Web Crypto) |
| 95 | +│ ├── keypair.js # X25519 keypair generation |
| 96 | +│ ├── keyStorage.js # Private key wrap/unwrap (Argon2id KEK + AES-GCM) |
| 97 | +│ └── messageEncryption.js # HPKE-Auth encrypt/decrypt |
| 98 | +├── blockchain/ |
| 99 | +│ └── blockchainVerifyView.js # Standalone Merkle root verification page (no login needed) |
| 100 | +└── images/ |
| 101 | + └── bows_background.png # Chat area background (optional) |
| 102 | +``` |
| 103 | + |
| 104 | +--- |
| 105 | + |
| 106 | +## Features |
| 107 | + |
| 108 | +| Feature | Notes | |
| 109 | +|---|---| |
| 110 | +| Sign up | Password must be 8+ chars with uppercase, lowercase, number, and special character | |
| 111 | +| Sign in | JWT stored in sessionStorage — cleared when tab closes | |
| 112 | +| Change password | Re-wraps private key under the new password, same strength rules | |
| 113 | +| Conversations | WhatsApp-style: contacts list on the left, active thread on the right | |
| 114 | +| Send messages | Encrypted client-side with HPKE-Auth before leaving the browser | |
| 115 | +| Receive & decrypt | Decrypted in the browser — server never sees plaintext | |
| 116 | +| Forward | Re-encrypts for a new recipient; shows their key fingerprint first | |
| 117 | +| Revoke | Blocks recipient's access; message stays visible to sender with "Revoked" badge | |
| 118 | +| Delete | Hard-deletes from the database; sender only | |
| 119 | +| Download | Saves message as a `.txt` file | |
| 120 | +| Key fingerprints | Shown in thread header for out-of-band identity verification | |
| 121 | +| TOFU pinning | First-seen key pinned in `localStorage`; key changes trigger a warning | |
| 122 | +| Blockchain verify | Public page at `#verify` — no login required | |
| 123 | + |
| 124 | +--- |
| 125 | + |
| 126 | +## Cryptographic Summary |
| 127 | + |
| 128 | +For the full threat model and primitive justifications see the cryptographic design document (to be submitted alongside the project). |
| 129 | + |
| 130 | +| Layer | Primitive | |
| 131 | +|---|---| |
| 132 | +| Key exchange | DHKEM(X25519) — two DH operations (sender auth) | |
| 133 | +| Key derivation | HKDF-SHA256 (RFC 5869) | |
| 134 | +| Encryption | AES-256-GCM with associated data | |
| 135 | +| Associated data | `{sender_id, recipient_id, message_id, timestamp}` (canonical JSON, no spaces) | |
| 136 | +| Password hashing | Argon2id (m=32768 KiB, t=2, p=4) | |
| 137 | +| Key at rest | AES-256-GCM wrapped private key | |
| 138 | + |
| 139 | +--- |
| 140 | + |
| 141 | +## Troubleshooting |
| 142 | + |
| 143 | +**Login page not visible / blank screen** |
| 144 | +- Open the browser DevTools console (`F12`) and check for JavaScript errors |
| 145 | +- Ensure the Flask server is running and accessible |
| 146 | + |
| 147 | +**Messages show as `(encrypted)`** |
| 148 | +- Both clients (web and C++) use the same wire format and key encoding — accounts created on either client are interoperable |
| 149 | +- The exception is the C++ Argon2id fallback path, which does not interoperate with the web client's key derivation |
| 150 | +- Check that `sender_x25519_public_key` is present in the server's `/messages` response |
| 151 | + |
| 152 | +**`argon2-browser` not found** |
| 153 | +- Run `npm install` inside the `client-web/` directory |
| 154 | + |
| 155 | +**CORS errors on the blockchain verify page** |
| 156 | +- The Sepolia RPC endpoint (`sepolia.gateway.tenderly.co`) must be CORS-enabled; the one configured in `index.html` supports browser requests |
0 commit comments