Add channel-aware delivery and update roadmap

This commit is contained in:
2026-02-22 06:31:16 +01:00
parent 9a3fa94858
commit d1ddef4cea
11 changed files with 374 additions and 85 deletions

View File

@@ -1,12 +1,12 @@
# quicnprotochat
> End-to-end encrypted group messaging over **Noise_XX + MLS** (RFC 9420), written in Rust.
> End-to-end encrypted group messaging over **QUIC + TLS 1.3 + MLS** (RFC 9420), written in Rust.
Every byte on the wire is double-protected: the outer **Noise_XX** channel
authenticates both sides and provides forward secrecy for the transport, while
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.
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.
---
@@ -18,16 +18,14 @@ key agreement across any number of participants. Messages are framed with
├─────────────────────────────────────────────┤
│ Cap'n Proto RPC │ <- typed, schema-versioned framing
├─────────────────────────────────────────────┤
Noise_XX (X25519 · ChaChaPoly · SHA-256) │ <- mutual auth + transport secrecy
├─────────────────────────────────────────────┤
│ TCP │
QUIC + TLS 1.3 (quinn/rustls) │ <- mutual auth + transport secrecy
└─────────────────────────────────────────────┘
```
| Property | Mechanism |
|---|---|
| Transport confidentiality | Noise_XX (ChaCha20-Poly1305) |
| Transport authentication | Noise_XX static X25519 keys |
| 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) |
@@ -40,12 +38,11 @@ key agreement across any number of participants. Messages are framed with
```
quicnprotochat/
├── crates/
│ ├── quicnprotochat-core/ # Crypto primitives, Noise transport, MLS group state machine
│ ├── quicnprotochat-core/ # Crypto primitives, QUIC/TLS client helpers, MLS group state machine
│ │ ├── src/codec.rs # LengthPrefixedCodec — Tokio Encoder + Decoder
│ │ ├── src/keypair.rs # NoiseKeypair — X25519 static key, zeroize-on-drop
│ │ ├── 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/noise.rs # handshake_initiator / handshake_responder / NoiseTransport
│ │ └── src/group.rs # GroupMember — full MLS group lifecycle
│ │
│ ├── quicnprotochat-proto/ # Cap'n Proto schemas + generated types + serde helpers
@@ -64,9 +61,10 @@ quicnprotochat/
## Services
### Authentication Service (AS) — port 7000
### Node Service (Auth + Delivery) — port 4201
Stores single-use MLS KeyPackages so peers can add each other to groups.
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)
@@ -76,9 +74,7 @@ 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.
### Delivery Service (DS) — port 7001
A simple store-and-forward relay for MLS messages. The DS never inspects
A simple store-and-forward relay for MLS messages. The server never inspects
payloads — it routes opaque blobs by recipient public key.
```
@@ -144,14 +140,23 @@ cargo test --workspace
## Running
**Start the server** (AS on :7000, DS on :7001):
**Start the server** (NodeService on :4201):
```bash
cargo run -p quicnprotochat-server
# or with custom ports:
cargo run -p quicnprotochat-server -- --listen 0.0.0.0:7000 --ds-listen 0.0.0.0:7001
# 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
@@ -167,21 +172,21 @@ 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:7000 \
--ds-server 127.0.0.1:7001
--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:7000
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:7000 --ds-server 127.0.0.1:7001
cargo run -p quicnprotochat-client -- join --state state.bin --ds-server 127.0.0.1:7001
cargo run -p quicnprotochat-client -- send --state state.bin --peer-key <peer hex> --msg "hello" --ds-server 127.0.0.1:7001
cargo run -p quicnprotochat-client -- recv --state state.bin --ds-server 127.0.0.1:7001
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:7000`; override with `--server` or
`QUICNPROTOCHAT_SERVER`. Delivery Service defaults to `127.0.0.1:7001`; override with
`--ds-server` or `QUICNPROTOCHAT_DS_SERVER`.
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
@@ -194,24 +199,44 @@ key is not retained; run join in the same session you register.
| # | Name | Status | What it adds |
|---|------|--------|--------------|
| M1 | Noise transport | ✅ | Noise_XX handshake, length-prefixed framing, Ping/Pong |
| 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 | 🔜 | ML-KEM-768 hybrid in Noise layer |
| 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 Noise keypair is **ephemeral** — regenerated on every restart.
Clients perform no server key pinning in the current milestone.
- 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 Service does no authentication of the `recipientKey` field —
- The Delivery operation does no authentication of the `recipientKey` field —
anyone can enqueue for any recipient. Access control is a future milestone.
---