# QUIC + TLS 1.3 quicprochat uses QUIC (RFC 9000) with mandatory TLS 1.3 (RFC 9001) as its transport layer. This page explains how the `quinn` and `rustls` crates are integrated and what security properties the transport provides. ## Why QUIC QUIC provides several advantages over traditional TCP-based transports: - **Multiplexed streams**: Native bidirectional streams; each RPC call gets its own stream without head-of-line blocking. - **0-RTT resumption**: Returning clients can send data in the first flight, reducing connection setup latency. - **Integrated encryption**: TLS 1.3 is integral to the QUIC handshake; no extra round-trips for transport security. - **NAT traversal**: UDP-based; connection migration survives NAT rebinding. - **Per-call concurrency**: The v2 RPC framework opens one bidirectional stream per RPC call. Multiple calls run concurrently without blocking each other. - **Push streams**: Server-to-client push events use QUIC uni-directional streams, avoiding any request-response overhead. ## Crate integration quicprochat uses the following crates for QUIC and TLS: - **`quinn 0.11`** -- The async QUIC implementation for Tokio. Provides `Endpoint`, `Connection`, and bidirectional/uni-directional stream types. - **`quinn-proto 0.11`** -- The protocol-level types, including `QuicServerConfig` and `QuicClientConfig` wrappers that bridge `rustls` into `quinn`. - **`rustls 0.23`** -- The TLS implementation. quicprochat uses it in strict TLS 1.3 mode with no fallback to TLS 1.2. - **`rcgen 0.13`** -- Self-signed certificate generation for development and testing. ### Server configuration The server builds its QUIC endpoint configuration with: ```rust let mut tls = rustls::ServerConfig::builder_with_protocol_versions(&[&TLS13]) .with_no_client_auth() .with_single_cert(cert_chain, key)?; tls.alpn_protocols = vec![b"qpc".to_vec()]; let crypto = QuicServerConfig::try_from(tls)?; Ok(ServerConfig::with_crypto(Arc::new(crypto))) ``` Key points: 1. **TLS 1.3 strict mode**: `builder_with_protocol_versions(&[&TLS13])` ensures no TLS 1.2 fallback. This is a hard requirement: TLS 1.2 lacks the 0-RTT and full forward secrecy guarantees that quicprochat relies on. 2. **No client certificate authentication**: `with_no_client_auth()` means the server does not verify client certificates at the TLS layer. Client authentication is handled at the application layer via OPAQUE password authentication and Ed25519 identity keys. This is a deliberate design choice -- OPAQUE provides stronger authentication properties than TLS client certificates without requiring PKI infrastructure. 3. **ALPN negotiation**: The Application-Layer Protocol Negotiation extension is set to `b"qpc"`, advertising that this endpoint speaks the quicprochat v2 Protobuf framing protocol. Both client and server must agree on this protocol identifier or the TLS handshake fails. 4. **`QuicServerConfig` bridge**: The `quinn-proto` crate provides `QuicServerConfig::try_from(tls)` to adapt the `rustls::ServerConfig` for use with QUIC. This handles the QUIC-specific TLS parameters (transport parameters, QUIC header protection keys) automatically. ### Client configuration The client performs the mirror operation. It loads the server's DER-encoded certificate from a local file and constructs a `rustls::ClientConfig`: ```rust let mut roots = rustls::RootCertStore::empty(); roots.add(CertificateDer::from(cert_bytes))?; let mut tls = rustls::ClientConfig::builder_with_protocol_versions(&[&TLS13]) .with_root_certificates(roots) .with_no_client_auth(); tls.alpn_protocols = vec![b"qpc".to_vec()]; let crypto = QuicClientConfig::try_from(tls)?; ``` The client trusts exactly one certificate: the server's self-signed cert loaded from disk. There is no system trust store involved, which simplifies the trust model but requires out-of-band distribution of the server certificate. ### Per-connection handling The v2 server accepts connections and handles streams concurrently: ```rust // Accept a QUIC connection let connection = endpoint.accept().await?; // For each incoming bidirectional stream (one per RPC call): let (send, recv) = connection.accept_bi().await?; // Read RequestFrame, dispatch, write ResponseFrame tokio::spawn(handle_rpc(send, recv, server_state)); // For server-initiated push events: let send = connection.open_uni().await?; // Write PushFrame tokio::spawn(send_push(send, event)); ``` Unlike the v1 Cap'n Proto RPC (which required `tokio::task::LocalSet` due to `!Send` internals), the v2 framework uses `Arc`-based shared state and `tokio::spawn` for full multi-threaded concurrency. ## Certificate trust model quicprochat currently uses a **trust-on-first-use (TOFU)** model with self-signed certificates: 1. On first start, the server generates a self-signed certificate using `rcgen::generate_simple_self_signed` with SANs for `localhost`, `127.0.0.1`, and `::1`. 2. The certificate and private key are persisted to disk as DER files (default: `data/server-cert.der` and `data/server-key.der`). 3. Clients must obtain the server's certificate file out-of-band and reference it via the `--ca-cert` flag or `QPC_CA_CERT` environment variable. This model is adequate for development and single-server deployments. The roadmap includes: - **ACME integration** (Let's Encrypt) for production deployments with publicly-routable servers. - **Certificate pinning** to detect MITM attacks even when a CA is compromised. - **Certificate transparency** log monitoring for detecting misissued certificates. ## Self-signed certificate generation The server's `generate_self_signed()` function: ```rust let subject_alt_names = vec![ "localhost".to_string(), "127.0.0.1".to_string(), "::1".to_string(), ]; let issued = generate_simple_self_signed(subject_alt_names)?; fs::write(cert_path, issued.cert.der())?; fs::write(key_path, &issued.key_pair.serialize_der())?; ``` The generated certificate includes both DNS and IP SANs so that clients can connect using either `localhost` or an IP address. The client specifies the expected server name via `--server-name` (default: `localhost`), which must match one of the certificate's SANs. ## Security properties The QUIC + TLS 1.3 layer provides: | Property | Mechanism | |---|---| | **Transport confidentiality** | All application data is encrypted with AES-128-GCM or ChaCha20-Poly1305 (negotiated during the TLS handshake) | | **Server authentication** | The client verifies the server's certificate against the locally-trusted DER file | | **Forward secrecy** | TLS 1.3 exclusively uses ephemeral Diffie-Hellman key exchange; session keys are not derivable from the server's long-term key | | **Replay protection** | QUIC packet numbers and TLS 1.3's anti-replay mechanism prevent replay attacks | | **Connection migration** | QUIC connection IDs allow the client to change IP addresses without re-handshaking | ### What TLS does *not* provide - **Client authentication**: Handled by OPAQUE password authentication (methods 100-103) and Ed25519 identity keys at the application layer. See [Service Architecture](../architecture/service-architecture.md). - **End-to-end encryption**: TLS terminates at the server. The server can read the Protobuf framing and message routing metadata. Payload confidentiality is provided by MLS. See [MLS (RFC 9420)](mls.md). - **Post-quantum resistance**: TLS 1.3 key exchange uses classical ECDHE. Post-quantum protection of application data is provided by the [Hybrid KEM](hybrid-kem.md) layer. ## Configuration reference ### Server | Environment Variable | CLI Flag | Default | Description | |---|---|---|---| | `QPC_LISTEN` | `--listen` | `0.0.0.0:5001` | QUIC listen address | | `QPC_TLS_CERT` | `--tls-cert` | `data/server-cert.der` | TLS certificate path | | `QPC_TLS_KEY` | `--tls-key` | `data/server-key.der` | TLS private key path | | `QPC_DATA_DIR` | `--data-dir` | `data` | Persistent storage directory | ### Client | Environment Variable | CLI Flag | Default | Description | |---|---|---|---| | `QPC_CA_CERT` | `--ca-cert` | `data/server-cert.der` | Server certificate to trust | | `QPC_SERVER_NAME` | `--server-name` | `localhost` | Expected TLS server name (must match certificate SAN) | | `QPC_SERVER` | `--server` | `127.0.0.1:5001` | Server address (per-subcommand) | ## Further reading - [Protobuf Framing](capn-proto.md) -- The RPC framing layer that runs on top of QUIC streams. - [Service Architecture](../architecture/service-architecture.md) -- How the server binds to the QUIC endpoint and dispatches 44 RPC methods.