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,171 @@
# Architecture Overview
quicnprotochat is an end-to-end encrypted group messaging system built in Rust.
This page describes the high-level architecture: the services that compose the
system, the dual-key cryptographic model, and how the pieces fit together.
---
## Two-Service Model
The server exposes two logical services through a single **NodeService** RPC
interface, bound to **port 7000** over QUIC + TLS 1.3:
| Logical Service | Responsibility |
|--------------------------|-----------------------------------------------------------------|
| **Authentication Service (AS)** | Stores and distributes single-use MLS KeyPackages. Clients upload KeyPackages after identity generation; peers fetch them to add new members to a group. |
| **Delivery Service (DS)** | Store-and-forward relay for opaque payloads. The DS never inspects MLS ciphertext -- it routes solely by recipient Ed25519 public key (and optional channel ID). |
Combining both services into a single endpoint simplifies deployment and
reduces round-trips. The schema is defined in
[`schemas/node.capnp`](../wire-format/node-service-schema.md) as a unified
`NodeService` interface.
See [Service Architecture](service-architecture.md) for per-method details,
connection lifecycle, and the long-polling `fetchWait` mechanism.
---
## Dual-Key Model
quicnprotochat uses two independent asymmetric key pairs per client, each
serving a distinct role:
```text
quicnprotochat Key Model
┌──────────────────────────────────────────────────┐
│ │
│ X25519 static keypair (Noise transport) │
│ ───────────────────────────────────── │
│ - Generated once per node identity │
│ - Used in the Noise_XX handshake (M1 stack) │
│ - Provides mutual authentication + │
│ channel confidentiality at the TCP layer │
│ - Classical only (no PQ protection) │
│ - Managed by NoiseKeypair, zeroize-on-drop │
│ │
│ Ed25519 signing keypair (MLS identity) │
│ ────────────────────────────────────── │
│ - Generated once per user/device │
│ - Embedded in MLS BasicCredential │
│ - Signs KeyPackages, Commits, and group ops │
│ - Raw 32-byte public key is the AS index │
│ - Managed by IdentityKeypair, zeroize-on-drop │
│ │
└──────────────────────────────────────────────────┘
```
| Property | X25519 (Noise) | Ed25519 (MLS) |
|-------------------|-------------------------------------|--------------------------------------------|
| Curve | Curve25519 (Montgomery) | Ed25519 (Twisted Edwards) |
| Purpose | Transport authentication + secrecy | Identity binding, signing, MLS credentials |
| Crate | `x25519-dalek` | `ed25519-dalek` |
| Zeroize on drop | Yes (`StaticSecret`) | Yes (`Zeroizing<[u8; 32]>`) |
| PQ protection | None (classical X25519) | MLS key schedule uses DHKEM(X25519); hybrid PQ KEM available at envelope level |
For details on the cryptographic properties of each key type, see
[Ed25519 Identity Keys](../cryptography/identity-keys.md) and
[X25519 Transport Keys](../cryptography/transport-keys.md).
---
## System Diagram
```text
┌─────────────────┐ ┌─────────────────┐
│ Alice Client │ │ Bob Client │
│ │ │ │
│ IdentityKeypair │ │ IdentityKeypair │
│ (Ed25519) │ │ (Ed25519) │
│ │ │ │
│ GroupMember │ │ GroupMember │
│ (MLS state) │ │ (MLS state) │
│ │ │ │
│ NoiseKeypair* │ │ NoiseKeypair* │
│ (X25519, M1) │ │ (X25519, M1) │
└────────┬─────────┘ └────────┬─────────┘
│ │
│ QUIC + TLS 1.3 (quinn/rustls) │
│ ─── or ─── │
│ Noise_XX over TCP (snow, M1 stack) │
│ │
▼ ▼
┌────────────────────────────────────────────────────────────────────────────┐
│ NodeService (port 7000) │
│ │
│ ┌──────────────────────────┐ ┌───────────────────────────────────┐ │
│ │ Authentication Service │ │ Delivery Service │ │
│ │ │ │ │ │
│ │ uploadKeyPackage() │ │ enqueue(recipientKey, payload) │ │
│ │ fetchKeyPackage() │ │ fetch(recipientKey) │ │
│ │ uploadHybridKey() │ │ fetchWait(recipientKey, timeout) │ │
│ │ fetchHybridKey() │ │ │ │
│ │ │ │ Queues: DashMap + FileBackedStore│ │
│ │ Store: DashMap + │ │ │ │
│ │ FileBackedStore │ │ │ │
│ └──────────────────────────┘ └───────────────────────────────────┘ │
│ │
│ health() │
│ │
└────────────────────────────────────────────────────────────────────────────┘
```
**Key observations:**
1. The server never sees plaintext message content. MLS ciphertext is opaque to
the DS -- it merely routes by `recipientKey`.
2. KeyPackages are single-use (RFC 9420 requirement). The AS atomically removes
a KeyPackage on fetch to enforce this invariant.
3. The QUIC + TLS 1.3 stack is the primary transport (M3+). The Noise_XX over
TCP stack from M1 remains available for environments where QUIC is blocked.
---
## Protocol Layering
The system stacks three protocol layers:
1. **Transport** -- QUIC + TLS 1.3 (primary) or Noise_XX over TCP (M1
fallback). Provides confidentiality, integrity, and server authentication.
See [Protocol Stack](protocol-stack.md).
2. **Framing / RPC** -- Cap'n Proto serialisation and RPC. Provides zero-copy
typed messages, schema versioning, and async method dispatch.
See [Cap'n Proto Serialisation and RPC](../protocol-layers/capn-proto.md).
3. **End-to-End Encryption** -- MLS (RFC 9420). Provides group key agreement,
forward secrecy, and post-compromise security. The server never holds group
keys.
See [MLS (RFC 9420)](../protocol-layers/mls.md).
An optional fourth layer -- the **hybrid KEM envelope** (X25519 + ML-KEM-768)
-- wraps MLS payloads for post-quantum confidentiality at the per-message level.
See [Hybrid KEM](../protocol-layers/hybrid-kem.md).
---
## Crate Map
The implementation is split across four workspace crates:
| Crate | Role |
|----------------------------|-------------------------------------------------------------------|
| `quicnprotochat-core` | Crypto primitives, Noise transport, MLS state machine, hybrid KEM |
| `quicnprotochat-proto` | Cap'n Proto schemas, codegen, and serialisation helpers |
| `quicnprotochat-server` | QUIC listener, NodeService RPC, storage |
| `quicnprotochat-client` | QUIC client, CLI subcommands, state persistence |
See [Crate Responsibilities](crate-responsibilities.md) for a full breakdown
and dependency diagram.
---
## Further Reading
- [Protocol Stack](protocol-stack.md) -- layered comparison of the two transport stacks
- [Service Architecture](service-architecture.md) -- NodeService RPC methods, connection lifecycle, long-polling
- [End-to-End Data Flow](data-flow.md) -- registration, group creation, and message exchange sequence diagrams
- [Wire Format Overview](../wire-format/overview.md) -- Cap'n Proto schema reference
- [Cryptography Overview](../cryptography/overview.md) -- detailed cryptographic properties and threat model