Files
quicproquo/README.md

247 lines
9.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# quicnprotochat
> End-to-end encrypted group messaging over **QUIC + TLS 1.3 + MLS** (RFC 9420), written in Rust.
Every byte on the wire is protected by a QUIC transport secured with TLS 1.3
(`quinn` + `rustls`). The inner **MLS** layer provides post-compromise security
and ratcheted group key agreement across any number of participants. Messages
are framed with **Cap'n Proto**, keeping serialisation zero-copy and
schema-versioned.
---
## Protocol stack
```
┌─────────────────────────────────────────────┐
│ Application / MLS ciphertext │ <- group key ratchet (RFC 9420)
├─────────────────────────────────────────────┤
│ Cap'n Proto RPC │ <- typed, schema-versioned framing
├─────────────────────────────────────────────┤
│ QUIC + TLS 1.3 (quinn/rustls) │ <- mutual auth + transport secrecy
└─────────────────────────────────────────────┘
```
| Property | Mechanism |
|---|---|
| Transport confidentiality | TLS 1.3 over QUIC (rustls) |
| Transport authentication | TLS 1.3 server cert (self-signed by default) |
| Group key agreement | MLS `MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519` |
| Post-compromise security | MLS epoch ratchet |
| Identity | Ed25519 (MLS credential + leaf node signature) |
| Message framing | Cap'n Proto (unpacked wire format) |
---
## Repository layout
```
quicnprotochat/
├── crates/
│ ├── quicnprotochat-core/ # Crypto primitives, QUIC/TLS client helpers, MLS group state machine
│ │ ├── src/codec.rs # LengthPrefixedCodec — Tokio Encoder + Decoder
│ │ ├── src/keypair.rs # Transport key helpers (X25519, zeroize-on-drop)
│ │ ├── src/identity.rs # IdentityKeypair — Ed25519 identity + MLS Signer
│ │ ├── src/keypackage.rs# generate_key_package — standalone KeyPackage helper
│ │ └── src/group.rs # GroupMember — full MLS group lifecycle
│ │
│ ├── quicnprotochat-proto/ # Cap'n Proto schemas + generated types + serde helpers
│ │ └── schemas/ → # (symlinked to workspace root schemas/)
│ │
│ ├── quicnprotochat-server/ # Authentication Service (AS) + Delivery Service (DS) binary
│ └── quicnprotochat-client/ # CLI client (ping, register, fetch-key, …)
└── schemas/
├── envelope.capnp # Top-level wire envelope (MsgType discriminant + payload)
├── auth.capnp # AuthenticationService RPC (KeyPackage upload / fetch)
└── delivery.capnp # DeliveryService RPC (enqueue / fetch MLS messages)
```
---
## Services
### Node Service (Auth + Delivery) — port 4201
Single QUIC + TLS 1.3 endpoint exposing Cap'n Proto `NodeService` that combines
Authentication (KeyPackage upload/fetch) and Delivery (enqueue/fetch) operations.
```
uploadKeyPackage(identityKey: Data, package: Data) -> (fingerprint: Data)
fetchKeyPackage(identityKey: Data) -> (package: Data)
```
Packages are indexed by the raw Ed25519 public key (32 bytes) and consumed
exactly once on fetch, matching the MLS single-use KeyPackage requirement.
A simple store-and-forward relay for MLS messages. The server never inspects
payloads — it routes opaque blobs by recipient public key.
```
enqueue(recipientKey: Data, payload: Data) -> ()
fetch(recipientKey: Data) -> (payloads: List(Data))
```
`fetch` atomically drains the entire queue in FIFO order.
---
## MLS group lifecycle
```
GroupMember::new(identity)
├─ generate_key_package() → upload bytes to AS
├─ create_group(group_id) → epoch 0, sole member
│ └─ add_member(kp_bytes)→ (commit_bytes, welcome_bytes)
│ ↑ │ │
│ fetched from AS discard send to joiner via DS
└─ join_group(welcome_bytes) → joined; ready to encrypt
├─ send_message(plain) → TLS-encoded PrivateMessage → DS
└─ receive_message(ct) → Some(plaintext) | None (Commit)
```
The `OpenMlsRustCrypto` backend is **persistent across calls** on the same
`GroupMember` instance — it holds the HPKE init private key in its in-memory
key store between `generate_key_package` and `join_group`.
---
## Building
**Prerequisites:**
- Rust (stable, 1.77+)
- `capnp` CLI — the Cap'n Proto schema compiler
```bash
# Debian / Ubuntu
apt-get install capnproto
# macOS
brew install capnp
```
**Build everything:**
```bash
cargo build --workspace
```
**Run tests:**
```bash
cargo test --workspace
```
---
## Running
**Start the server** (NodeService on :4201):
```bash
cargo run -p quicnprotochat-server
# or with a custom port:
cargo run -p quicnprotochat-server -- --listen 0.0.0.0:4201
```
Current TLS defaults (development): self-signed cert/key written to `data/` if
missing. Override via CLI flags or env vars:
| Purpose | Flag | Env var | Default |
|---|---|---|---|
| Listen address | `--listen` | `QUICNPROTOCHAT_LISTEN` | `0.0.0.0:4201` |
| TLS cert (DER) | `--tls-cert` | `QUICNPROTOCHAT_TLS_CERT` | `data/server-cert.der` |
| TLS key (DER) | `--tls-key` | `QUICNPROTOCHAT_TLS_KEY` | `data/server-key.der` |
**Client commands:**
```bash
# Check connectivity
cargo run -p quicnprotochat-client -- ping
# Generate a fresh identity + KeyPackage, upload to AS
# Prints your identity_key (hex) — share this with peers
cargo run -p quicnprotochat-client -- register
# Fetch a peer's KeyPackage (they must have registered first)
cargo run -p quicnprotochat-client -- fetch-key <64-hex-char identity key>
# Run an end-to-end Alice↔Bob demo against live AS + DS
cargo run -p quicnprotochat-client -- demo-group \
--server 127.0.0.1:4201 \
--ds-server 127.0.0.1:4201
# Persistent group CLI (stateful)
cargo run -p quicnprotochat-client -- register-state --state state.bin --server 127.0.0.1:4201
cargo run -p quicnprotochat-client -- create-group --state state.bin --group-id my-group
cargo run -p quicnprotochat-client -- invite --state state.bin --peer-key <peer hex> --server 127.0.0.1:4201 --ds-server 127.0.0.1:4201
cargo run -p quicnprotochat-client -- join --state state.bin --ds-server 127.0.0.1:4201
cargo run -p quicnprotochat-client -- send --state state.bin --peer-key <peer hex> --msg "hello" --ds-server 127.0.0.1:4201
cargo run -p quicnprotochat-client -- recv --state state.bin --ds-server 127.0.0.1:4201
```
Server address defaults to `127.0.0.1:4201`; override with `--server` or
`QUICNPROTOCHAT_SERVER`. The same endpoint serves both Authentication and
Delivery.
State file notes: the persisted state stores your identity and MLS group state
after you have joined. If you generate a KeyPackage (`register-state`) and then
restart before consuming the Welcome, the join may fail because the HPKE init
key is not retained; run join in the same session you register.
---
## Milestones
| # | Name | Status | What it adds |
|---|------|--------|--------------|
| M1 | QUIC/TLS transport | ✅ | QUIC + TLS 1.3 endpoint, length-prefixed framing, Ping/Pong |
| M2 | Authentication Service | ✅ | Ed25519 identity, KeyPackage generation, AS upload/fetch |
| M3 | Delivery Service + MLS groups | ✅ | DS relay, `GroupMember` create/join/add/send/recv |
| M4 | Group CLI subcommands | 🔜 | Persistent CLI (`create-group`, `invite`, `join`, `send`, `recv`); demo-group already available |
| M5 | Multi-party groups | 🔜 | N > 2 members, Commit fan-out, Proposal handling |
| M6 | Persistence | 🔜 | SQLite key store, durable group state |
| M7 | Post-quantum | 🔜 | PQ hybrid for MLS/HPKE |
---
## Production hardening roadmap (high level)
1) **Transport & identity**: ACME/Lets Encrypt, pinned identities, TLS policy
hardening, server identity via CA.
2) **Persistence**: Move AS/DS and MLS state to Postgres; encrypted at rest;
retention/TTL and migrations.
3) **AuthZ & accounts**: User/device accounts (OIDC/passwordless), device
binding, revocation/recovery; bind MLS credentials to issued identities.
4) **Delivery semantics**: Message IDs, idempotent enqueue/fetch, ordering per
conversation, backpressure/retries; attachment pipeline via encrypted
object storage.
5) **Observability & ops**: Structured logs with correlation IDs; Prometheus
metrics; tracing; alerting + SLOs; audit logs for auth/key events.
6) **Client resilience**: Reconnect/resume, offline queue, multi-device key
handling; key verification UX (QR/safety numbers); recovery flows.
7) **Security & compliance**: Dependency audits, fuzzing, SAST/DAST, pentest;
SBOM/signed releases; PII minimization and retention controls.
---
## Security notes
- This is a **proof-of-concept**. It has not been audited.
- The server uses a self-signed TLS cert by default; clients trust it via a
local DER file. No pinning or CA-based identity is enforced yet.
- MLS credentials use `CredentialType::Basic` (public key only). A real
deployment would bind credentials to a certificate authority.
- The Delivery operation does no authentication of the `recipientKey` field —
anyone can enqueue for any recipient. Access control is a future milestone.
---
## License
MIT