feat: add post-quantum hybrid KEM + SQLCipher persistence

Feature 1 — Post-Quantum Hybrid KEM (X25519 + ML-KEM-768):
- Create hybrid_kem.rs with keygen, encrypt, decrypt + 11 unit tests
- Wire format: version(1) | x25519_eph_pk(32) | mlkem_ct(1088) | nonce(12) | ct
- Add uploadHybridKey/fetchHybridKey RPCs to node.capnp schema
- Server: hybrid key storage in FileBackedStore + RPC handlers
- Client: hybrid keypair in StoredState, auto-wrap/unwrap in send/recv/invite/join
- demo-group runs full hybrid PQ envelope round-trip

Feature 2 — SQLCipher Persistence:
- Extract Store trait from FileBackedStore API
- Create SqlStore (rusqlite + bundled-sqlcipher) with encrypted-at-rest SQLite
- Schema: key_packages, deliveries, hybrid_keys tables with indexes
- Server CLI: --store-backend=sql, --db-path, --db-key flags
- 5 unit tests for SqlStore (FIFO, round-trip, upsert, channel isolation)

Also includes: client lib.rs refactor, auth config, TOML config file support,
mdBook documentation, and various cleanups by user.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-22 08:07:48 +01:00
parent d1ddef4cea
commit f334ed3d43
81 changed files with 14502 additions and 2289 deletions

View File

@@ -0,0 +1,239 @@
# Testing Strategy
This page describes the testing structure, conventions, and current coverage for
quicnprotochat. All tests run with `cargo test --workspace` and must pass before
any code is merged.
For the coding standards that tests must follow, see
[Coding Standards](coding-standards.md).
---
## Test Organisation
### Unit Tests
Unit tests live alongside the code they test, in `#[cfg(test)] mod tests` blocks
at the bottom of each source file. They test individual functions and types in
isolation.
**quicnprotochat-core:**
| Module | Tests | What they cover |
|--------|-------|----------------|
| `codec` | 7 tests | Length-prefixed frame encoding/decoding, edge cases (empty payload, max size, partial frame, exact boundary) |
| `keypair` | 3 tests | Ed25519 keypair generation, public key extraction, deterministic re-derivation |
| `group` | 2 tests | Group round-trip (create + add + join + send + recv), group\_id lifecycle |
| `hybrid_kem` | 11 tests | Encapsulate/decapsulate round-trip, key generation, combiner correctness, wrong-key rejection, serialisation |
**quicnprotochat-proto:**
| Module | Tests | What they cover |
|--------|-------|----------------|
| `lib` | 3 tests | Cap'n Proto builder/reader round-trip, canonical serialisation, schema validation |
### Integration Tests
Integration tests live in `crates/quicnprotochat-client/tests/` and test the
full client-server interaction. Each test spawns a server using `tokio::spawn`
within the same test binary, then runs client operations against it.
| File | Milestone | What it covers |
|------|-----------|---------------|
| `noise_transport.rs` | M1 | Noise\_XX handshake over TCP, Ping/Pong frame exchange, connection lifecycle |
| `auth_service.rs` | M2 | KeyPackage upload via AS, KeyPackage fetch (single-use consume semantics), identity key validation |
| `mls_group.rs` | M3 | Full MLS round-trip: register state, create group, add member via Welcome, send encrypted message, receive and decrypt |
### Test Pattern
All integration tests follow the same pattern:
```rust
#[tokio::test]
async fn test_something() {
// 1. Start server in background
let server_handle = tokio::spawn(async move {
server::run(config).await.unwrap();
});
// 2. Wait for server to be ready
tokio::time::sleep(Duration::from_millis(100)).await;
// 3. Run client operations
let result = client::do_something(server_addr).await;
// 4. Assert
assert!(result.is_ok());
// ...
// 5. Cleanup
server_handle.abort();
}
```
This pattern ensures tests are self-contained and do not require an external
server process.
---
## Running Tests
### Full Workspace
```bash
cargo test --workspace
```
This runs all unit tests and integration tests across all four crates.
### Single Crate
```bash
cargo test -p quicnprotochat-core
cargo test -p quicnprotochat-proto
cargo test -p quicnprotochat-server
cargo test -p quicnprotochat-client
```
### Single Test
```bash
cargo test -p quicnprotochat-core -- codec::tests::test_round_trip
cargo test -p quicnprotochat-client --test mls_group
```
### With Output
```bash
cargo test --workspace -- --nocapture
```
---
## Current Results
All tests pass as of the M3 milestone on branch `feat/m1-noise-transport`.
Summary:
| Crate | Unit Tests | Integration Tests | Total |
|-------|-----------|-------------------|-------|
| `quicnprotochat-core` | 23 | -- | 23 |
| `quicnprotochat-proto` | 3 | -- | 3 |
| `quicnprotochat-server` | 0 | -- | 0 |
| `quicnprotochat-client` | 0 | 6 | 6 |
| **Total** | **26** | **6** | **32** |
---
## Test Conventions
### Naming
Test functions use descriptive names that state what is being tested and the
expected outcome:
```rust
#[test]
fn encode_decode_round_trip_preserves_payload() { ... }
#[test]
fn empty_payload_produces_length_zero_frame() { ... }
#[test]
fn fetch_consumes_keypackage_single_use() { ... }
```
### Assertions
- Use `assert_eq!` with both expected and actual values.
- Use `assert!(result.is_ok(), "descriptive message: {result:?}")` for
`Result` checks.
- For crypto operations, assert on specific error variants, not just
`is_err()`.
### No External Dependencies
Tests must not depend on external services, network access, or filesystem state
outside the test's temporary directory. The `tokio::spawn` pattern for
client-server tests ensures everything runs in-process.
### Determinism
Tests must be deterministic. If randomness is needed (e.g., key generation),
the test must not depend on specific random values -- only on the properties of
the output (correct length, successful round-trip, etc.).
---
## Planned Testing Enhancements
The following testing improvements are planned for future milestones:
### Fuzzing Targets (M5+)
Fuzz testing for parser and deserialisation code:
- **Cap'n Proto message parser:** Feed arbitrary bytes to the Cap'n Proto reader
and verify it either parses correctly or returns a typed error (no panics,
no undefined behaviour).
- **MLS message handler:** Feed arbitrary `MLSMessage` bytes to the
`GroupMember::receive_message` path.
- **Length-prefixed codec:** Fuzz the frame decoder with arbitrary byte streams.
Tool: `cargo-fuzz` with `libfuzzer`.
### Golden-Wire Fixtures (M5+)
Serialised test vectors for regression testing across versions:
- Capture the wire bytes of known-good Cap'n Proto messages (Envelope, Auth,
Delivery structs) at the current version.
- Store as `.bin` files in `tests/fixtures/`.
- Each test deserialises the fixture and verifies the expected field values.
- When the wire format changes, fixtures are updated with a version bump.
This catches accidental wire-format regressions that would break client-server
compatibility.
### N-1 Compatibility Tests (M5+)
Test that a client built at version N can communicate with a server built at
version N-1 (and vice versa):
- Build two versions of the binary (current and previous release).
- Run the older server with the newer client and verify all RPCs succeed.
- Run the newer server with the older client and verify graceful degradation
(legacy mode works, new features return clean errors).
### Criterion Benchmarks (M5)
Performance benchmarks using [Criterion.rs](https://docs.rs/criterion/):
- Key generation latency (Ed25519, X25519, ML-KEM-768).
- MLS encap/decap (KeyPackage generation, Welcome processing).
- Group-add latency scaling: 2, 10, 100, 1000 members.
- Cap'n Proto serialise/deserialise throughput.
- Noise handshake latency.
Benchmarks run separately from tests (`cargo bench`) and are not part of the
CI gate, but are tracked for regression detection.
### Docker-based E2E Tests (Phase 5)
End-to-end tests using `testcontainers-rs` (see
[Future Research: Testcontainers-rs](../roadmap/future-research.md#testcontainers-rs)):
- Spin up server container from the Docker image.
- Run client operations from the test process against the containerised server.
- Verify real network boundaries, container startup, and multi-process
interactions.
---
## Cross-references
- [Coding Standards](coding-standards.md) -- quality requirements for test code
- [Milestones](../roadmap/milestones.md) -- which tests were added at each milestone
- [Production Readiness WBS](../roadmap/production-readiness.md) -- Phase 5 (E2E Harness and Security Tests)
- [Future Research: Testcontainers-rs](../roadmap/future-research.md#testcontainers-rs) -- Docker-based testing