UAT/PHASE1.md — 20 test scenarios, 80+ checkboxes Identity, encryption, messaging, TUI, web, groups, aliases, auth, OTP replenishment, session persistence, cross-client UAT/PHASE2.md — 7 scenarios (WASM, receipts, files, multi-device, HW wallet, groups, history) UAT/PHASE3.md — 6 scenarios (DNS discovery, key transparency, federation, mutual TLS, gossip) UAT/PHASE4.md — 10 scenarios (mule identity, pickup, delivery, receipts, dedup, expiry, compression) UAT/PHASE5.md — 6 scenarios (Bluetooth, LoRa, mDNS, Wi-Fi Direct, USB export, fallback chain) UAT/PHASE6.md — 3 scenarios (sealed sender, traffic analysis resistance, onion routing) UAT/PHASE7.md — 8 scenarios (ntfy, DoH, DB encryption, admin CLI, rate limiting, audit, CI, monitoring) Each test has exact commands to run and checkboxes for pass/fail. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
554 lines
12 KiB
Markdown
554 lines
12 KiB
Markdown
# 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 <paste 24 words from Alice's init>
|
|
```
|
|
|
|
- [ ] "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/<bob-fingerprint-no-colons>
|
|
```
|
|
|
|
- [ ] 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 "<bob-fingerprint>" "Hello from Alice" -s http://localhost:7700
|
|
```
|
|
|
|
- [ ] "No existing session. Fetching key bundle for ..."
|
|
- [ ] "Message sent to <bob-fingerprint>"
|
|
|
|
**T3 (Bob receives):**
|
|
```bash
|
|
WARZONE_HOME=/tmp/bob cargo run --bin warzone-client -- recv -s http://localhost:7700
|
|
```
|
|
|
|
- [ ] "Received 1 message(s):"
|
|
- [ ] `[new session] <alice-fingerprint>: Hello from Alice`
|
|
|
|
**Bob sends reply:**
|
|
```bash
|
|
WARZONE_HOME=/tmp/bob cargo run --bin warzone-client -- send "<alice-fingerprint>" "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] <bob-fingerprint>: 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 "<bob-fingerprint>" -s http://localhost:7700
|
|
```
|
|
|
|
**T3 (Bob):**
|
|
```bash
|
|
WARZONE_HOME=/tmp/bob cargo run --bin warzone-client -- chat "<alice-fingerprint>" -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 → <bob-fingerprint>" resolved
|
|
- [ ] "Peer set to <bob-fingerprint>"
|
|
|
|
**List aliases:**
|
|
```
|
|
/aliases
|
|
```
|
|
|
|
- [ ] Shows `@alice → <fp>` and `@bob → <fp>`
|
|
|
|
---
|
|
|
|
## 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":"<alice-fp-no-colons>"}'
|
|
```
|
|
|
|
- [ ] 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":"<saved-key>","new_fingerprint":"<bob-fp-no-colons>"}'
|
|
```
|
|
|
|
- [ ] "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":"<alice-fp-no-colons>"}'
|
|
```
|
|
|
|
- [ ] 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/<alice-fp-no-colons>/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":"<alice-fp-no-colons>","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/<alice-fp-no-colons>/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 "<bob-fp>" "message 1" -s http://localhost:7700
|
|
cargo run --bin warzone-client -- send "<bob-fp>" "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: _______________
|