Remove Noise protocol references from wiki docs and tests
Delete 8 Noise-specific documentation pages (noise-xx.md,
transport-keys.md, adr-001/003/006, framing-codec.md) and update
~30 remaining wiki pages to reflect QUIC+TLS as the sole transport.
Remove obsolete Noise-based integration tests (auth_service.rs,
mls_group.rs). Code-side Noise removal was done in f334ed3.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,118 +0,0 @@
|
||||
# ADR-001: Noise\_XX for Transport Authentication
|
||||
|
||||
**Status:** Accepted
|
||||
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
quicnprotochat needs mutual authentication at the transport layer: both client and server must prove their identity before any application data is exchanged. The standard solution is TLS with X.509 certificates, but this brings significant operational complexity:
|
||||
|
||||
- A Certificate Authority (CA) must be operated or purchased from.
|
||||
- Certificates must be provisioned, rotated, and revoked.
|
||||
- Client certificate authentication in TLS is cumbersome and poorly supported by many libraries.
|
||||
- The X.509 PKI is a large attack surface with a long history of CA compromises.
|
||||
|
||||
An alternative is needed that provides mutual authentication with simpler key management, ideally using raw public keys rather than certificates.
|
||||
|
||||
### Alternatives considered
|
||||
|
||||
1. **TLS 1.3 with X.509 certificates.** Standard, widely deployed, but requires CA infrastructure. Client certificate authentication is possible but adds complexity. Later adopted for the QUIC transport (M3+), where server authentication is sufficient and client auth is handled at the application layer via the `Auth` struct.
|
||||
|
||||
2. **TLS 1.3 with Raw Public Keys (RFC 7250).** Eliminates the CA dependency but has limited library support. The `rustls` crate did not support RPK at the time of the M1 design.
|
||||
|
||||
3. **Noise Protocol Framework.** Purpose-built for authenticated key exchange using raw static keys. Multiple handshake patterns available. Mature specification with formal security analysis. Well-supported by the `snow` crate in Rust.
|
||||
|
||||
4. **WireGuard-style handshake.** Based on Noise\_IK. Assumes the initiator already knows the responder's static key. Does not provide identity hiding for the initiator.
|
||||
|
||||
---
|
||||
|
||||
## Decision
|
||||
|
||||
Use the **Noise\_XX handshake pattern** for the M1 transport layer. Both parties hold static X25519 keypairs that are registered out-of-band (e.g., via a future directory service, QR code, or manual configuration).
|
||||
|
||||
### Why Noise\_XX specifically?
|
||||
|
||||
The Noise Protocol Framework defines several handshake patterns, differing in which static keys are transmitted during the handshake:
|
||||
|
||||
| Pattern | Initiator static key | Responder static key | Identity hiding |
|
||||
|---|---|---|---|
|
||||
| **NN** | Not transmitted | Not transmitted | No authentication |
|
||||
| **NK** | Not transmitted | Known to initiator | Server-only auth |
|
||||
| **KK** | Known to responder | Known to initiator | Mutual auth, no identity hiding |
|
||||
| **XX** | Transmitted (encrypted) | Transmitted (encrypted) | **Mutual auth + identity hiding for initiator** |
|
||||
| **IK** | Transmitted (encrypted) | Known to initiator | Mutual auth, initiator identity hidden from passive observers |
|
||||
|
||||
**XX** was chosen because:
|
||||
|
||||
1. **Mutual authentication.** Both parties prove possession of their static private keys during the handshake. The server verifies the client's identity, and the client verifies the server's identity.
|
||||
|
||||
2. **Identity hiding for the initiator.** The initiator's static public key is transmitted encrypted under an ephemeral key, so a passive network observer cannot determine who is connecting. The responder's static key is also transmitted encrypted, though an active attacker performing a man-in-the-middle on the first message could learn it (this is inherent to any pattern where the responder's key is not pre-known).
|
||||
|
||||
3. **No pre-shared keys required.** Unlike IK or KK, the XX pattern does not require either party to know the other's static key before the handshake begins. This simplifies bootstrapping: a new client can connect to a server without prior key exchange.
|
||||
|
||||
4. **Three-message handshake.** XX completes in 3 messages (-> e, <- e ee s es, -> s se), which is one round-trip more than IK but provides stronger identity hiding guarantees.
|
||||
|
||||
### Cryptographic parameters
|
||||
|
||||
| Parameter | Value |
|
||||
|---|---|
|
||||
| Handshake pattern | `Noise_XX_25519_ChaChaPoly_SHA256` |
|
||||
| DH function | X25519 (Curve25519) |
|
||||
| AEAD cipher | ChaCha20-Poly1305 |
|
||||
| Hash function | SHA-256 |
|
||||
| Static key size | 32 bytes (X25519 public key) |
|
||||
| Ephemeral key size | 32 bytes (X25519 public key) |
|
||||
|
||||
### Implementation
|
||||
|
||||
The Noise handshake is implemented using the `snow` crate (`snow 0.9`). Key source files:
|
||||
|
||||
- `crates/quicnprotochat-core/src/noise.rs` -- `NoiseTransport` struct, handshake state machine, encrypted read/write methods.
|
||||
- `crates/quicnprotochat-core/src/codec.rs` -- `LengthPrefixedCodec` that frames Noise handshake and transport messages over TCP.
|
||||
- `crates/quicnprotochat-core/src/error.rs` -- `CoreError::Noise` variant for handshake and transport errors.
|
||||
|
||||
---
|
||||
|
||||
## Consequences
|
||||
|
||||
### Benefits
|
||||
|
||||
- **No CA infrastructure.** Key management is reduced to generating, storing, and distributing raw 32-byte X25519 public keys. No certificates, no expiration, no revocation lists.
|
||||
- **Simpler key management.** Each node has a single static X25519 keypair. The public key is its transport-layer identity.
|
||||
- **Identity hiding.** Passive network observers cannot determine which client is connecting to the server.
|
||||
- **Well-analyzed security.** The Noise Protocol Framework has formal security proofs (Kobeissi et al., 2019). The XX pattern specifically has been analyzed for identity hiding and key compromise impersonation resistance.
|
||||
- **Lightweight.** The `snow` crate is small, auditable, and has no transitive dependency on OpenSSL or ring (it uses pure-Rust cryptography).
|
||||
|
||||
### Costs and trade-offs
|
||||
|
||||
- **Three-message handshake.** XX requires 3 messages (1.5 round-trips) compared to TLS 1.3's 1-RTT handshake (or 0-RTT with resumption). This adds latency to connection establishment. In practice, this is only significant for short-lived connections.
|
||||
- **No PQ protection.** The Noise handshake uses classical X25519. A quantum adversary performing a harvest-now-decrypt-later attack could recover the handshake transcript and learn the static keys. This is accepted as a known risk (see [ADR-006: PQ Gap](adr-006-pq-gap.md)).
|
||||
- **Out-of-band key distribution.** Without a CA or directory service, clients must obtain the server's static public key through some out-of-band mechanism. This is currently handled by hardcoding or configuration.
|
||||
- **Superseded for client-server transport.** With the move to QUIC + TLS 1.3 in M3, the Noise transport is no longer the primary client-server path. It remains available for direct peer-to-peer connections and as a fallback in environments where QUIC/UDP is blocked.
|
||||
|
||||
### Residual risks
|
||||
|
||||
- **Harvest-now-decrypt-later for metadata.** An adversary who records the Noise handshake today and obtains a quantum computer in the future could decrypt the handshake transcript, revealing the static public keys of both parties (identity metadata). However, no long-lived content secrets transit the handshake -- MLS provides its own key agreement. See [ADR-006](adr-006-pq-gap.md) for the full analysis.
|
||||
- **Key compromise impersonation (KCI).** If a party's static private key is compromised, an attacker can impersonate other parties to the compromised party. This is inherent to any DH-based mutual authentication scheme without a PKI. Mitigated by key rotation and secure key storage.
|
||||
|
||||
---
|
||||
|
||||
## Code references
|
||||
|
||||
| File | Relevance |
|
||||
|---|---|
|
||||
| `crates/quicnprotochat-core/src/noise.rs` | `NoiseTransport` implementation: handshake, encrypted read/write |
|
||||
| `crates/quicnprotochat-core/src/codec.rs` | `LengthPrefixedCodec`: frames Noise messages over TCP |
|
||||
| `crates/quicnprotochat-core/src/error.rs` | `CoreError::Noise`, `CodecError` error types |
|
||||
|
||||
---
|
||||
|
||||
## Further reading
|
||||
|
||||
- [Design Decisions Overview](overview.md) -- index of all ADRs
|
||||
- [ADR-003: RPC Inside the Noise Tunnel](adr-003-rpc-inside-noise.md) -- how Cap'n Proto RPC runs over the Noise channel
|
||||
- [ADR-006: PQ Gap in Noise Transport](adr-006-pq-gap.md) -- analysis of the post-quantum gap
|
||||
- [Framing Codec](../wire-format/framing-codec.md) -- the codec that frames Noise messages
|
||||
- [Protocol Layers Overview](../protocol-layers/overview.md) -- how Noise fits in the protocol stack
|
||||
- [Noise Protocol Framework specification](https://noiseprotocol.org/noise.html) -- upstream specification
|
||||
@@ -135,6 +135,5 @@ The Cap'n Proto schemas are stored in the `schemas/` directory:
|
||||
|
||||
- [Design Decisions Overview](overview.md) -- index of all ADRs
|
||||
- [Wire Format Overview](../wire-format/overview.md) -- how Cap'n Proto fits in the serialisation pipeline
|
||||
- [ADR-003: RPC Inside the Noise Tunnel](adr-003-rpc-inside-noise.md) -- how Cap'n Proto RPC runs over the encrypted transport
|
||||
- [Why This Design, Not Signal/Matrix/...](why-not-signal.md) -- serialisation comparison against Protobuf and JSON
|
||||
- [Cap'n Proto encoding specification](https://capnproto.org/encoding.html) -- upstream specification
|
||||
|
||||
@@ -1,147 +0,0 @@
|
||||
# ADR-003: RPC Inside the Noise Tunnel
|
||||
|
||||
**Status:** Accepted
|
||||
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
Cap'n Proto RPC provides typed method dispatch, promise pipelining, and automatic serialisation -- but it has **no built-in transport security**. The RPC protocol assumes it operates over a trusted byte stream. If that byte stream is a raw TCP connection, all RPC traffic (method names, parameters, return values) is transmitted in cleartext.
|
||||
|
||||
quicnprotochat requires that all client-server communication be encrypted and authenticated. The question is: how should encryption and RPC be composed?
|
||||
|
||||
### Alternatives considered
|
||||
|
||||
1. **RPC over raw TCP, with application-level encryption.** Each RPC payload would be individually encrypted by the application before passing it to Cap'n Proto. This is complex, error-prone, and does not protect RPC metadata (method ordinals, message structure).
|
||||
|
||||
2. **RPC over TLS.** Use TLS 1.3 as the transport for the Cap'n Proto RPC byte stream. This is the conventional approach for web services (gRPC uses TLS). However, in the M1 design, TLS with mutual authentication required CA infrastructure that we wanted to avoid (see [ADR-001](adr-001-noise-xx.md)).
|
||||
|
||||
3. **RPC over Noise.** Use the Noise\_XX handshake to establish an encrypted, authenticated session, then feed the Cap'n Proto RPC byte stream through the Noise transport layer. The RPC layer is completely unaware of the encryption beneath it.
|
||||
|
||||
4. **RPC over QUIC.** Use QUIC + TLS 1.3 as the transport. Cap'n Proto RPC operates over a QUIC bidirectional stream. This is the approach adopted in M3+.
|
||||
|
||||
---
|
||||
|
||||
## Decision
|
||||
|
||||
Cap'n Proto RPC operates over the encrypted byte stream provided by the transport layer. The transport layer -- whether Noise\_XX (M1) or QUIC + TLS 1.3 (M3+) -- owns all security properties (confidentiality, integrity, authentication). Cap'n Proto owns all framing and dispatch properties (serialisation, method routing, schema enforcement).
|
||||
|
||||
This is a **separation of concerns** at the protocol layer boundary:
|
||||
|
||||
```text
|
||||
┌─────────────────────────────────┐
|
||||
│ Cap'n Proto RPC │ Dispatch, serialisation, typing
|
||||
│ (capnp-rpc crate) │
|
||||
├─────────────────────────────────┤
|
||||
│ Encrypted byte stream │ Confidentiality, integrity, auth
|
||||
│ (Noise_XX or QUIC/TLS 1.3) │
|
||||
├─────────────────────────────────┤
|
||||
│ TCP or UDP │ Reliable (TCP) or datagram (UDP)
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Noise transport path (M1)
|
||||
|
||||
In the M1 stack, the composition works as follows:
|
||||
|
||||
1. Client and server perform a Noise\_XX handshake over a TCP connection, establishing a shared session key.
|
||||
2. The resulting `NoiseTransport` wraps the TCP stream, providing `AsyncRead + AsyncWrite` that transparently encrypts/decrypts all data.
|
||||
3. Cap'n Proto RPC is instantiated over this `NoiseTransport`. The RPC runtime reads and writes to the `NoiseTransport` as if it were a plain byte stream.
|
||||
4. Each RPC message is framed by the [LengthPrefixedCodec](../wire-format/framing-codec.md) before encryption and after decryption.
|
||||
|
||||
```text
|
||||
Client Server
|
||||
| |
|
||||
| --- Noise_XX handshake (3 messages) -----------> |
|
||||
| <-- Noise_XX handshake ------------------------- |
|
||||
| |
|
||||
| [Noise-encrypted Cap'n Proto RPC traffic] |
|
||||
| --- uploadKeyPackage(identityKey, pkg, auth) --> |
|
||||
| <-- (fingerprint) -------------------------------- |
|
||||
| --- enqueue(recipientKey, payload, ch, v, a) --> |
|
||||
| <-- () ------------------------------------------ |
|
||||
| ... |
|
||||
```
|
||||
|
||||
### QUIC transport path (M3+)
|
||||
|
||||
In the M3+ stack, the composition is:
|
||||
|
||||
1. Client connects to the server via QUIC, which performs a TLS 1.3 handshake internally.
|
||||
2. The client opens a bidirectional QUIC stream.
|
||||
3. Cap'n Proto RPC is instantiated over the QUIC stream. The `quinn` crate provides `AsyncRead + AsyncWrite` for each stream.
|
||||
4. The `LengthPrefixedCodec` is **not used** in this path -- QUIC provides native stream framing, and `capnp-rpc` handles message delimitation internally.
|
||||
|
||||
```text
|
||||
Client Server
|
||||
| |
|
||||
| --- QUIC handshake (TLS 1.3) -----------------> |
|
||||
| <-- QUIC handshake ---------------------------- |
|
||||
| |
|
||||
| [QUIC-encrypted Cap'n Proto RPC traffic] |
|
||||
| --- uploadKeyPackage(identityKey, pkg, auth) --> |
|
||||
| <-- (fingerprint) -------------------------------- |
|
||||
| --- fetchWait(recipientKey, ch, v, t, a) ------> |
|
||||
| <-- (payloads) ---------------------------------- |
|
||||
| ... |
|
||||
```
|
||||
|
||||
### Transport agnosticism
|
||||
|
||||
The key architectural property is that **Cap'n Proto RPC is transport-agnostic**. The same RPC interface (`NodeService`) works identically over both transport paths. The server implementation does not know or care which transport the client used -- it receives the same typed method calls either way.
|
||||
|
||||
This is achieved by abstracting the transport behind Rust's `AsyncRead + AsyncWrite` traits. The `capnp-rpc` crate accepts any type that implements these traits as its underlying stream.
|
||||
|
||||
---
|
||||
|
||||
## Consequences
|
||||
|
||||
### Benefits
|
||||
|
||||
- **Clean layering.** Each layer has a single, well-defined responsibility. The transport layer does not need to understand Cap'n Proto. Cap'n Proto does not need to understand encryption. This makes each layer independently testable and replaceable.
|
||||
|
||||
- **Transport flexibility.** Switching from Noise to QUIC (or adding a future transport) required no changes to the RPC interface or the application logic. Only the transport initialization code changed.
|
||||
|
||||
- **Full metadata protection.** Because encryption wraps the entire RPC byte stream, not just individual payloads, all RPC metadata is protected: method ordinals, parameter values, return values, and even the timing pattern of RPC calls (within the limits of the transport's traffic analysis resistance).
|
||||
|
||||
- **No double encryption.** The application layer does not need to encrypt RPC payloads separately. The transport layer provides confidentiality for the entire stream.
|
||||
|
||||
- **Composable security.** The Noise/QUIC layer provides transport security (server authentication, channel confidentiality). MLS provides end-to-end security (group key agreement, forward secrecy, PCS). The RPC layer is the bridge between them, carrying MLS ciphertext as opaque blobs. No single layer needs to provide all security properties.
|
||||
|
||||
### Costs and trade-offs
|
||||
|
||||
- **No end-to-end RPC security.** The RPC layer trusts the transport for confidentiality. If the transport is compromised (e.g., a TLS vulnerability), all RPC traffic is exposed. This is mitigated by MLS providing a second layer of encryption for message content.
|
||||
|
||||
- **Transport must be established first.** The Noise handshake or QUIC connection must complete before any RPC call can be made. This adds latency to the first interaction. In the QUIC path, this is mitigated by 0-RTT resumption.
|
||||
|
||||
- **Debugging complexity.** Because all traffic is encrypted, debugging wire-level issues requires either decrypting the transport (which requires the session keys) or logging at the application layer. This is an inherent trade-off of transport encryption.
|
||||
|
||||
### Residual risks
|
||||
|
||||
- **Transport-layer vulnerability.** A vulnerability in `snow` (Noise) or `rustls` (TLS) could expose the RPC byte stream. Mitigated by keeping dependencies updated and by the fact that MLS ciphertext within the stream is independently encrypted.
|
||||
|
||||
- **Side channels.** The transport encrypts content but may not fully hide message sizes or timing patterns. A sophisticated adversary could infer information from traffic analysis. This is a known limitation of any encrypted transport and is orthogonal to the RPC-inside-transport decision.
|
||||
|
||||
---
|
||||
|
||||
## Code references
|
||||
|
||||
| File | Relevance |
|
||||
|---|---|
|
||||
| `crates/quicnprotochat-core/src/noise.rs` | `NoiseTransport`: encrypted `AsyncRead + AsyncWrite` wrapper |
|
||||
| `crates/quicnprotochat-core/src/codec.rs` | `LengthPrefixedCodec`: frames messages in the Noise path |
|
||||
| `crates/quicnprotochat-server/src/main.rs` | Server: accepts QUIC connections, instantiates Cap'n Proto RPC over QUIC streams |
|
||||
| `crates/quicnprotochat-client/src/main.rs` | Client: connects via QUIC, instantiates Cap'n Proto RPC client |
|
||||
| `schemas/node.capnp` | `NodeService` RPC interface definition |
|
||||
|
||||
---
|
||||
|
||||
## Further reading
|
||||
|
||||
- [Design Decisions Overview](overview.md) -- index of all ADRs
|
||||
- [ADR-001: Noise\_XX for Transport Auth](adr-001-noise-xx.md) -- the Noise transport that RPC runs inside (M1)
|
||||
- [ADR-002: Cap'n Proto over MessagePack](adr-002-capnproto.md) -- why Cap'n Proto was chosen for serialisation
|
||||
- [Wire Format Overview](../wire-format/overview.md) -- the full serialisation pipeline
|
||||
- [Framing Codec](../wire-format/framing-codec.md) -- length-prefixed framing in the Noise path
|
||||
- [NodeService Schema](../wire-format/node-service-schema.md) -- the RPC interface that runs over the encrypted tunnel
|
||||
- [Protocol Layers Overview](../protocol-layers/overview.md) -- how all protocol layers compose
|
||||
@@ -1,119 +0,0 @@
|
||||
# ADR-006: PQ Gap in Noise Transport
|
||||
|
||||
**Status:** Accepted
|
||||
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
quicnprotochat's security architecture has two encryption layers:
|
||||
|
||||
1. **Transport layer** (Noise\_XX or QUIC + TLS 1.3): encrypts the byte stream between client and server using classical Diffie-Hellman key exchange (X25519).
|
||||
2. **Content layer** (MLS, RFC 9420): provides end-to-end group key agreement using DHKEM(X25519, HKDF-SHA256) in the current ciphersuite, with a hybrid KEM (X25519 + ML-KEM-768) available at the envelope level and planned for integration into the MLS ciphersuite at M5.
|
||||
|
||||
The content layer will have post-quantum protection from M5 onward via the hybrid KEM. However, the transport layer uses classical X25519 exclusively. This creates a **post-quantum gap**: the transport layer is vulnerable to a quantum adversary, even after the content layer is PQ-protected.
|
||||
|
||||
### The threat: harvest-now, decrypt-later
|
||||
|
||||
A quantum adversary who does not yet have a cryptographically relevant quantum computer (CRQC) can still:
|
||||
|
||||
1. **Record** all encrypted traffic transiting the network today.
|
||||
2. **Store** the recordings until a CRQC becomes available.
|
||||
3. **Decrypt** the recorded traffic using Shor's algorithm to break X25519.
|
||||
|
||||
This is known as the "harvest-now, decrypt-later" (HNDL) attack. The question is: **what is the practical impact of HNDL on quicnprotochat's transport layer?**
|
||||
|
||||
### What a quantum adversary learns from breaking the transport
|
||||
|
||||
If the Noise\_XX handshake is broken, the adversary learns:
|
||||
|
||||
| Data | Sensitivity | Exposure |
|
||||
|---|---|---|
|
||||
| Static X25519 public keys of both parties | Identity metadata | Reveals which client connected to which server |
|
||||
| Timing and size of RPC calls | Traffic metadata | Reveals communication patterns |
|
||||
| Cap'n Proto RPC traffic (method calls, parameters) | Routing metadata | Reveals recipient keys, channel IDs, and message timestamps |
|
||||
| MLS ciphertext (payload bytes) | **Still encrypted** | MLS uses its own key agreement; breaking the transport does not break MLS |
|
||||
|
||||
Critically, **no long-lived content secrets transit the Noise handshake**. The MLS key schedule derives group keys independently of the transport. Even with full transport decryption, the adversary sees only MLS ciphertext, which they cannot decrypt without breaking MLS's own key exchange (which will be PQ-protected from M5).
|
||||
|
||||
### Why not use PQ-Noise now?
|
||||
|
||||
The Noise Protocol Framework community has drafted extensions for post-quantum Noise (PQ-Noise), which replace or augment X25519 with PQ key exchange mechanisms (e.g., Kyber/ML-KEM). However:
|
||||
|
||||
1. **The `snow` crate does not support PQ-Noise.** As of snow 0.9, there is no API for PQ handshake patterns. Adding PQ support would require forking `snow` or switching to a different Noise implementation.
|
||||
|
||||
2. **PQ-Noise is not yet standardized.** The draft specifications (e.g., `draft-noise-pq`) are still evolving. Adopting an unstable specification risks incompatibility with future versions.
|
||||
|
||||
3. **Performance and size concerns.** ML-KEM-768 ciphertexts are 1,088 bytes, and encapsulation keys are 1,184 bytes. These are significantly larger than X25519's 32-byte keys. In a Noise handshake, where multiple key exchanges occur, the handshake size and latency increase substantially.
|
||||
|
||||
4. **The QUIC path uses TLS 1.3.** The primary transport in M3+ is QUIC + TLS 1.3, which has its own PQ migration path (via `rustls` and the `x25519-mlkem768` TLS key exchange group). This path is more likely to receive PQ support before `snow` does.
|
||||
|
||||
---
|
||||
|
||||
## Decision
|
||||
|
||||
Accept the PQ gap in the Noise transport layer for milestones M1 through M5. The content layer (MLS) will be PQ-protected from M5 via the hybrid KEM. The transport layer will gain PQ protection when either:
|
||||
|
||||
- The `snow` crate adds PQ-Noise support, or
|
||||
- The QUIC/TLS path gains PQ key exchange support via `rustls`, or
|
||||
- A PQ-Noise Rust implementation becomes available and is adopted.
|
||||
|
||||
Until then, the transport layer uses classical X25519, and the PQ gap is accepted as a known, bounded risk.
|
||||
|
||||
---
|
||||
|
||||
## Consequences
|
||||
|
||||
### What is protected
|
||||
|
||||
- **Message content** is protected by MLS's own key agreement. Even if the transport is broken, MLS ciphertext remains secure (assuming MLS uses a PQ-safe ciphersuite, which is the plan for M5).
|
||||
- **MLS key material** (epoch secrets, application secrets) never transits the Noise handshake. They are derived from the MLS tree, not from the transport.
|
||||
- **Forward secrecy of content** is provided by MLS epoch ratcheting, independent of the transport.
|
||||
|
||||
### What is exposed
|
||||
|
||||
- **Identity metadata.** A quantum adversary who breaks the Noise handshake learns the static X25519 public keys of both parties. This reveals *which* client connected to *which* server, and *when*.
|
||||
- **Timing metadata.** The adversary learns the timing and size pattern of RPC calls, which can reveal communication patterns (e.g., "Alice and Bob exchanged messages at 3pm").
|
||||
- **Routing metadata.** The adversary learns the recipient keys and channel IDs in RPC calls (since Cap'n Proto RPC traffic is visible after transport decryption). This reveals *who* is communicating with *whom*, even though the message content remains encrypted by MLS.
|
||||
|
||||
### Practical impact assessment
|
||||
|
||||
| Risk Factor | Assessment |
|
||||
|---|---|
|
||||
| **Timeline to CRQC** | Most estimates place cryptographically relevant quantum computers at 10-20+ years away. The PQ gap is a near-term risk only for adversaries with very long storage horizons. |
|
||||
| **Value of metadata** | Identity and timing metadata is sensitive for high-value targets but less critical than message content for most users. |
|
||||
| **Content protection** | Message content is independently protected by MLS. Breaking the transport does not break content encryption. |
|
||||
| **Migration path** | PQ key exchange for TLS 1.3 is being standardized (ML-KEM in TLS). The QUIC/TLS path is likely to gain PQ protection before the Noise path. |
|
||||
| **Overall risk** | **Low to moderate.** The PQ gap exposes metadata only, not content. The risk is limited to adversaries who (a) can record traffic today, (b) will have a CRQC in the future, and (c) are interested in metadata about quicnprotochat users. |
|
||||
|
||||
### Mitigation timeline
|
||||
|
||||
| Milestone | Transport PQ Status | Content PQ Status |
|
||||
|---|---|---|
|
||||
| M1 | Classical X25519 (Noise) | Classical DHKEM (MLS) |
|
||||
| M2 | Classical X25519 (Noise) | Classical DHKEM (MLS) |
|
||||
| M3 | Classical X25519 (QUIC/TLS 1.3) | Classical DHKEM (MLS) + hybrid KEM at envelope level |
|
||||
| M4 | Classical X25519 (QUIC/TLS 1.3) | Classical DHKEM (MLS) + hybrid KEM at envelope level |
|
||||
| M5 | Classical X25519 (QUIC/TLS 1.3) | **PQ-protected** (hybrid KEM integrated into MLS ciphersuite) |
|
||||
| Future | **PQ-protected** (PQ key exchange in TLS or PQ-Noise) | PQ-protected |
|
||||
|
||||
---
|
||||
|
||||
## Code references
|
||||
|
||||
| File | Relevance |
|
||||
|---|---|
|
||||
| `crates/quicnprotochat-core/src/noise.rs` | Noise\_XX handshake using classical X25519 (`Noise_XX_25519_ChaChaPoly_SHA256`) |
|
||||
| `crates/quicnprotochat-core/src/hybrid_kem.rs` | Hybrid KEM (X25519 + ML-KEM-768) for content-layer PQ protection |
|
||||
| `crates/quicnprotochat-server/src/main.rs` | QUIC server using `rustls` with classical TLS 1.3 |
|
||||
| `crates/quicnprotochat-client/src/main.rs` | QUIC client using `rustls` with classical TLS 1.3 |
|
||||
|
||||
---
|
||||
|
||||
## Further reading
|
||||
|
||||
- [Design Decisions Overview](overview.md) -- index of all ADRs
|
||||
- [ADR-001: Noise\_XX for Transport Auth](adr-001-noise-xx.md) -- the Noise transport that has the PQ gap
|
||||
- [Why This Design, Not Signal/Matrix/...](why-not-signal.md) -- PQ readiness comparison across protocols
|
||||
- [Protocol Layers Overview](../protocol-layers/overview.md) -- how transport and content layers compose
|
||||
- [Noise Protocol Framework specification](https://noiseprotocol.org/noise.html) -- upstream Noise specification
|
||||
@@ -10,12 +10,9 @@ These decisions are not immutable. Each ADR has a status field and can be supers
|
||||
|
||||
| ADR | Title | Status | One-line summary |
|
||||
|---|---|---|---|
|
||||
| [ADR-001](adr-001-noise-xx.md) | Noise\_XX for Transport Auth | Accepted | Mutual authentication via static X25519 keys; no CA infrastructure required. |
|
||||
| [ADR-002](adr-002-capnproto.md) | Cap'n Proto over MessagePack | Accepted | Zero-copy, schema-enforced serialisation with built-in async RPC replaces hand-rolled MessagePack dispatch. |
|
||||
| [ADR-003](adr-003-rpc-inside-noise.md) | RPC Inside the Noise Tunnel | Accepted | Cap'n Proto RPC operates over the encrypted byte stream; transport owns security, RPC owns dispatch. |
|
||||
| [ADR-004](adr-004-mls-unaware-ds.md) | MLS-Unaware Delivery Service | Accepted | The DS routes opaque blobs by recipient key; it never inspects MLS content. |
|
||||
| [ADR-005](adr-005-single-use-keypackages.md) | Single-Use KeyPackages | Accepted | The AS atomically removes a KeyPackage on fetch to preserve MLS forward secrecy. |
|
||||
| [ADR-006](adr-006-pq-gap.md) | PQ Gap in Noise Transport | Accepted | Classical X25519 in Noise is accepted for M1-M5; MLS content is PQ-protected separately. |
|
||||
|
||||
---
|
||||
|
||||
@@ -43,7 +40,7 @@ Several themes recur across multiple ADRs:
|
||||
|
||||
### Layered security
|
||||
|
||||
ADR-001, ADR-003, and ADR-006 all concern the separation between transport-layer security (Noise or QUIC/TLS) and application-layer security (MLS). The core principle is that **no single layer is trusted alone**. Transport encryption protects metadata and provides authentication; MLS provides end-to-end content encryption with forward secrecy and post-compromise security.
|
||||
The core principle is that **no single layer is trusted alone**. QUIC/TLS transport encryption protects metadata and provides authentication; MLS provides end-to-end content encryption with forward secrecy and post-compromise security.
|
||||
|
||||
### Server minimalism
|
||||
|
||||
@@ -51,7 +48,7 @@ ADR-004 and ADR-005 reflect a design philosophy where the server does as little
|
||||
|
||||
### Schema-first design
|
||||
|
||||
ADR-002 and ADR-003 establish Cap'n Proto as the single source of truth for the wire format. Every message and RPC call is defined in `.capnp` schema files, which are checked into the repository and used for code generation. This eliminates the class of bugs that arises from hand-rolled serialisation and ensures that the wire format is documented, versioned, and evolvable.
|
||||
ADR-002 establishes Cap'n Proto as the single source of truth for the wire format. Every message and RPC call is defined in `.capnp` schema files, which are checked into the repository and used for code generation. This eliminates the class of bugs that arises from hand-rolled serialisation and ensures that the wire format is documented, versioned, and evolvable.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ The transport layer determines how encrypted payloads reach the server and how c
|
||||
| **Head-of-line blocking** | Mitigated by HTTP/2 streams, but TCP HOL blocking remains | Same as Signal | Eliminated: QUIC streams are independent at the transport layer |
|
||||
| **Connection establishment** | 1-RTT (TLS 1.3) or 0-RTT (TLS resumption) | 1-RTT (TLS 1.3) or 0-RTT | 0-RTT capable (QUIC resumption) or 1-RTT |
|
||||
| **Client authentication** | Bearer tokens over TLS | Bearer tokens over TLS | TLS client certs (rustls/quinn) or bearer tokens via `Auth` struct |
|
||||
| **Fallback** | TCP only | TCP only | Noise\_XX over TCP (M1 stack) for environments where UDP/QUIC is blocked |
|
||||
| **Fallback** | TCP only | TCP only | None currently (QUIC only) |
|
||||
|
||||
**Why QUIC?**
|
||||
|
||||
@@ -136,7 +136,7 @@ While Protobuf is a reasonable choice (and Signal uses it successfully), Cap'n P
|
||||
| **Group op cost** | O(n) to O(n^2) | O(n) | **O(log n)** |
|
||||
| **Transport** | TLS/TCP (HTTP/2) | TLS/TCP (HTTPS) | **QUIC/UDP** (0-RTT, no HOL blocking) |
|
||||
| **Serialisation** | Protobuf | JSON | **Cap'n Proto** (zero-copy, canonical, built-in RPC) |
|
||||
| **Standardization** | De facto standard | Matrix spec (open, community-governed) | **IETF RFC 9420** (MLS) + Noise Protocol Framework |
|
||||
| **Standardization** | De facto standard | Matrix spec (open, community-governed) | **IETF RFC 9420** (MLS) |
|
||||
| **Federation** | No (centralized) | Yes (decentralized) | No (single server per deployment) |
|
||||
| **PQ readiness** | PQXDH (X3DH + ML-KEM) in 1:1, not in groups | Not yet | Hybrid KEM (X25519 + ML-KEM-768) at envelope layer; MLS PQ integration planned (M5) |
|
||||
| **Maturity** | 10+ years, billions of users | 7+ years, millions of users | Early development (M1-M3) |
|
||||
@@ -157,7 +157,6 @@ No design is without trade-offs. Compared to Signal and Matrix, quicnprotochat:
|
||||
## Further reading
|
||||
|
||||
- [Design Decisions Overview](overview.md) -- index of all ADRs
|
||||
- [ADR-001: Noise\_XX for Transport Auth](adr-001-noise-xx.md) -- transport authentication choice
|
||||
- [ADR-002: Cap'n Proto over MessagePack](adr-002-capnproto.md) -- serialisation format choice
|
||||
- [Protocol Layers Overview](../protocol-layers/overview.md) -- how quicnprotochat's layers compose
|
||||
- [MLS (RFC 9420)](../protocol-layers/mls.md) -- deep dive into the MLS protocol layer
|
||||
|
||||
Reference in New Issue
Block a user