# Phase 1 — User Acceptance Testing ## Prerequisites ```bash cd warzone cargo build rm -rf warzone-data # clean server DB ``` Open 3 terminals: - **T1**: Server - **T2**: Alice (default `~/.warzone`) - **T3**: Bob (`WARZONE_HOME=/tmp/bob`) --- ## 1. Server Startup **T1:** ```bash cargo run --bin warzone-server ``` - [ ] Server prints "Listening on 0.0.0.0:7700" - [ ] `curl http://localhost:7700/v1/health` returns `{"status":"ok","version":"0.1.0"}` - [ ] `http://localhost:7700/` loads the web UI in a browser --- ## 2. Identity Generation **T2 (Alice):** ```bash cargo run --bin warzone-client -- init ``` - [ ] Prompted "Set passphrase (empty for no encryption):" - [ ] Input is hidden (no echo) - [ ] Prompted "Confirm passphrase:" - [ ] Fingerprint displayed in format `xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx` - [ ] 24-word BIP39 mnemonic displayed - [ ] Seed path shown (e.g. `/Users/you/.warzone/identity.seed`) - [ ] "Generated 1 signed pre-key + 10 one-time pre-keys" shown - [ ] File `~/.warzone/identity.seed` exists - [ ] File `~/.warzone/bundle.bin` exists - [ ] File permissions on identity.seed are 600 (Unix): `ls -la ~/.warzone/identity.seed` **T3 (Bob):** ```bash WARZONE_HOME=/tmp/bob cargo run --bin warzone-client -- init ``` - [ ] Bob gets a different fingerprint than Alice - [ ] Seed saved to `/tmp/bob/identity.seed` - [ ] Bob's mnemonic is different from Alice's --- ## 3. Seed Encryption **T2 (Alice):** ```bash cargo run --bin warzone-client -- info ``` - [ ] Prompted for passphrase (if one was set during init) - [ ] Fingerprint, signing key, and encryption key displayed - [ ] Same fingerprint as during init - [ ] Wrong passphrase shows "Wrong passphrase" error **Test plaintext seed (empty passphrase):** ```bash WARZONE_HOME=/tmp/test cargo run --bin warzone-client -- init # press Enter twice for empty passphrase xxd /tmp/test/identity.seed | head -1 ``` - [ ] File is exactly 32 bytes (raw seed, no encryption header) **Test encrypted seed:** ```bash xxd ~/.warzone/identity.seed | head -1 ``` - [ ] File starts with `575a 5331` (hex for "WZS1" magic bytes) - [ ] File is larger than 32 bytes (salt + nonce + ciphertext) --- ## 4. Mnemonic Recovery ```bash WARZONE_HOME=/tmp/recovered cargo run --bin warzone-client -- recover ``` - [ ] "Identity recovered!" shown - [ ] Fingerprint matches Alice's original fingerprint - [ ] `WARZONE_HOME=/tmp/recovered cargo run --bin warzone-client -- info` shows same keys --- ## 5. Key Registration **T2 (Alice):** ```bash cargo run --bin warzone-client -- register -s http://localhost:7700 ``` - [ ] "Bundle registered with http://localhost:7700" **T3 (Bob):** ```bash WARZONE_HOME=/tmp/bob cargo run --bin warzone-client -- register -s http://localhost:7700 ``` - [ ] "Bundle registered with http://localhost:7700" **Verify on server:** ```bash curl http://localhost:7700/v1/keys/list ``` - [ ] JSON shows 2 keys with Alice's and Bob's fingerprints (hex, no colons) **Verify lookup works:** ```bash curl http://localhost:7700/v1/keys/ ``` - [ ] Returns JSON with `fingerprint` and `bundle` (base64 string) - [ ] Does NOT return 404 --- ## 6. 1:1 E2E Encrypted Messaging (CLI) **T2 (Alice sends to Bob):** ```bash cargo run --bin warzone-client -- send "" "Hello from Alice" -s http://localhost:7700 ``` - [ ] "No existing session. Fetching key bundle for ..." - [ ] "Message sent to " **T3 (Bob receives):** ```bash WARZONE_HOME=/tmp/bob cargo run --bin warzone-client -- recv -s http://localhost:7700 ``` - [ ] "Received 1 message(s):" - [ ] `[new session] : Hello from Alice` **Bob sends reply:** ```bash WARZONE_HOME=/tmp/bob cargo run --bin warzone-client -- send "" "Hi Alice, Bob here" -s http://localhost:7700 ``` - [ ] "Message sent to ..." (no "new session" — reuses existing ratchet) **Alice receives:** ```bash cargo run --bin warzone-client -- recv -s http://localhost:7700 ``` - [ ] `[new session] : Hi Alice, Bob here` --- ## 7. Fetch-and-Delete (No Duplicate Delivery) **T3 (Bob polls again):** ```bash WARZONE_HOME=/tmp/bob cargo run --bin warzone-client -- recv -s http://localhost:7700 ``` - [ ] "No new messages." (Alice's message was deleted on first poll) --- ## 8. TUI Chat (CLI) **T2 (Alice):** ```bash cargo run --bin warzone-client -- chat "" -s http://localhost:7700 ``` **T3 (Bob):** ```bash WARZONE_HOME=/tmp/bob cargo run --bin warzone-client -- chat "" -s http://localhost:7700 ``` - [ ] Both TUIs launch with header showing fingerprints - [ ] Alice types "hello from TUI" → Enter - [ ] Message appears in green on Alice's screen - [ ] Within 2 seconds, message appears in yellow on Bob's screen - [ ] Bob types "reply from Bob" → Enter - [ ] Message appears on both screens - [ ] `/info` shows fingerprint - [ ] `/quit` exits TUI cleanly (terminal restored) - [ ] Ctrl+C also exits cleanly - [ ] Esc also exits cleanly --- ## 9. Groups (CLI TUI) **T2 (Alice, in TUI):** ``` /g ops ``` - [ ] "Joined 'ops'" or "Group 'ops' auto-created" - [ ] "Switched to group #ops" **T3 (Bob, in TUI):** ``` /g ops ``` - [ ] "Joined 'ops'" - [ ] "Switched to group #ops" **Alice types a message:** ``` hello team ``` - [ ] Message appears on Alice's screen with `[#ops]` tag - [ ] Message appears on Bob's screen within 2 seconds **Bob replies:** ``` hey alice! ``` - [ ] Appears on both screens **Test group list:** ``` /glist ``` - [ ] Shows `#ops (2 members)` **Switch back to DM:** ``` /dm ``` - [ ] "Switched to DM mode" --- ## 10. Aliases (CLI TUI) **T2 (Alice, in TUI):** ``` /alias alice ``` - [ ] "Alias @alice registered" **T3 (Bob, in TUI):** ``` /alias bob ``` - [ ] "Alias @bob registered" **Alice sets peer by alias:** ``` /peer @bob ``` - [ ] "@bob → " resolved - [ ] "Peer set to " **List aliases:** ``` /aliases ``` - [ ] Shows `@alice → ` and `@bob → ` --- ## 11. Web UI — Identity Open `http://localhost:7700/` in a browser. - [ ] "WARZONE" title and "Generate Identity" button shown - [ ] Click "Generate Identity" - [ ] Fingerprint displayed in green - [ ] Hex seed displayed in orange - [ ] "Enter Chat" button shown - [ ] Click "Enter Chat" - [ ] Chat screen loads with header showing fingerprint - [ ] "Key registered with server" message appears - [ ] Refresh page → auto-loads identity (no setup screen) --- ## 12. Web UI — DM Open TWO browser tabs/windows (or incognito for second identity). **Tab 1:** Generate identity → Enter Chat **Tab 2:** Generate identity → Enter Chat **Tab 1:** Paste Tab 2's fingerprint in peer input field. Type "hello from tab 1". Enter. - [ ] Message appears in green on Tab 1 - [ ] Message appears with lock icon on Tab 2 within 2 seconds **Tab 2:** Paste Tab 1's fingerprint. Type "hello back". Enter. - [ ] Message appears on both tabs --- ## 13. Web UI — Groups **Tab 1:** ``` /g webteam ``` - [ ] "Joined group" and "Switched to group" messages **Tab 2:** ``` /g webteam ``` - [ ] Also joined **Tab 1:** Type "hello webteam" → Enter - [ ] Message appears on Tab 1 with `[webteam]` tag - [ ] Message appears on Tab 2 within 2 seconds --- ## 14. Web UI — Aliases **Tab 1:** ``` /alias webuser1 ``` - [ ] "Alias @webuser1 registered" **Tab 1:** ``` /info ``` - [ ] Shows fingerprint with `(@webuser1)` suffix **Tab 2:** Set peer input to `@webuser1`. Type message. Enter. - [ ] Message delivered (alias resolved to fingerprint) --- ## 15. Alias TTL & Recovery **Register alias via curl:** ```bash curl -X POST http://localhost:7700/v1/alias/register \ -H 'Content-Type: application/json' \ -d '{"alias":"testuser","fingerprint":""}' ``` - [ ] Response includes `recovery_key` (32-char hex) - [ ] Response includes `expires_in_days: 365` - [ ] **SAVE THE RECOVERY KEY** **Check alias:** ```bash curl http://localhost:7700/v1/alias/resolve/testuser ``` - [ ] Returns fingerprint + `expires_in_days` **Recover alias to new fingerprint:** ```bash curl -X POST http://localhost:7700/v1/alias/recover \ -H 'Content-Type: application/json' \ -d '{"alias":"testuser","recovery_key":"","new_fingerprint":""}' ``` - [ ] "ok: true" - [ ] `new_recovery_key` returned (rotated) **Verify transfer:** ```bash curl http://localhost:7700/v1/alias/resolve/testuser ``` - [ ] Now points to Bob's fingerprint **Wrong recovery key:** ```bash curl -X POST http://localhost:7700/v1/alias/recover \ -H 'Content-Type: application/json' \ -d '{"alias":"testuser","recovery_key":"wrong","new_fingerprint":"aaaa"}' ``` - [ ] "error: invalid recovery key" --- ## 16. Server Auth (Challenge-Response) **Request challenge:** ```bash curl -X POST http://localhost:7700/v1/auth/challenge \ -H 'Content-Type: application/json' \ -d '{"fingerprint":""}' ``` - [ ] Returns `challenge` (64-char hex) and `expires_at` (unix timestamp) - [ ] Challenge expires in ~60 seconds --- ## 17. OTP Key Replenishment **Check count:** ```bash curl http://localhost:7700/v1/keys//otpk-count ``` - [ ] Returns `otpk_count` (number, may be 0 if not yet stored separately) **Replenish:** ```bash curl -X POST http://localhost:7700/v1/keys/replenish \ -H 'Content-Type: application/json' \ -d '{"fingerprint":"","otpks":[{"id":100,"public_key":"aa"},{"id":101,"public_key":"bb"}]}' ``` - [ ] Returns `stored: 2` and `total` count **Verify count increased:** ```bash curl http://localhost:7700/v1/keys//otpk-count ``` - [ ] `otpk_count` increased by 2 --- ## 18. Protocol Unit Tests ```bash cargo test -p warzone-protocol ``` - [ ] `identity::tests::deterministic_derivation` — PASS - [ ] `identity::tests::mnemonic_roundtrip` — PASS - [ ] `identity::tests::fingerprint_display` — PASS - [ ] `mnemonic::tests::roundtrip` — PASS - [ ] `crypto::tests::aead_roundtrip` — PASS - [ ] `crypto::tests::aead_wrong_key_fails` — PASS - [ ] `crypto::tests::aead_wrong_aad_fails` — PASS - [ ] `crypto::tests::hkdf_deterministic` — PASS - [ ] `prekey::tests::signed_pre_key_verify` — PASS - [ ] `prekey::tests::signed_pre_key_reject_tampered` — PASS - [ ] `prekey::tests::generate_otpks` — PASS - [ ] `x3dh::tests::x3dh_shared_secret_matches` — PASS - [ ] `ratchet::tests::basic_exchange` — PASS - [ ] `ratchet::tests::bidirectional` — PASS - [ ] `ratchet::tests::multiple_messages_same_direction` — PASS - [ ] `ratchet::tests::out_of_order` — PASS - [ ] `ratchet::tests::many_messages` — PASS **Total: 17/17 PASS** --- ## 19. Session Persistence **T2 (Alice, send then quit):** ```bash cargo run --bin warzone-client -- send "" "message 1" -s http://localhost:7700 cargo run --bin warzone-client -- send "" "message 2" -s http://localhost:7700 ``` - [ ] First send says "No existing session" (X3DH) - [ ] Second send does NOT say "No existing session" (reuses saved ratchet) - [ ] `ls ~/.warzone/db/` shows sled database files **T3 (Bob receives both):** ```bash WARZONE_HOME=/tmp/bob cargo run --bin warzone-client -- recv -s http://localhost:7700 ``` - [ ] Both messages decrypted correctly - [ ] Messages in order --- ## 20. Cross-Client Compatibility **Web → CLI:** Web Tab sends message to CLI Alice's fingerprint. - [ ] CLI `recv` shows `[encrypted message from CLI client — use CLI to read]` OR fails gracefully - [ ] No crash on either side **CLI → Web:** CLI Alice sends to Web Tab's fingerprint. - [ ] Web shows graceful error (different crypto) or ignores silently - [ ] No crash on either side **Note:** Web↔CLI interop requires WASM bridge (Phase 2). Currently incompatible crypto is expected. --- ## Summary | # | Feature | Result | |---|---------|--------| | 1 | Server startup | ☐ | | 2 | Identity generation | ☐ | | 3 | Seed encryption | ☐ | | 4 | Mnemonic recovery | ☐ | | 5 | Key registration | ☐ | | 6 | 1:1 E2E messaging | ☐ | | 7 | Fetch-and-delete | ☐ | | 8 | TUI chat | ☐ | | 9 | Groups (CLI) | ☐ | | 10 | Aliases (CLI) | ☐ | | 11 | Web UI identity | ☐ | | 12 | Web UI DM | ☐ | | 13 | Web UI groups | ☐ | | 14 | Web UI aliases | ☐ | | 15 | Alias TTL & recovery | ☐ | | 16 | Server auth | ☐ | | 17 | OTP replenishment | ☐ | | 18 | Protocol tests (17/17) | ☐ | | 19 | Session persistence | ☐ | | 20 | Cross-client compat | ☐ | **Tester:** _______________ **Date:** _______________ **Build:** `cargo build` commit hash: _______________