QUIC: A UDP-Based Multiplexed and Secure Transport
Extracted elements (31)
Only clients can migrate in QUIC version 1; servers cannot initiate migration. This asymmetry simplifies the protocol because the client is the party whose network path changes most commonly (e.g., mobile devices), while allowing server-preferred address migration as a limited exception.
QUIC uses three separate packet number spaces (Initial, Handshake, 1-RTT) so that acknowledgments for packets at one encryption level cannot be confused with another, and to cleanly separate key epochs. ACK frames only acknowledge packets in the same number space.
Transport parameter values used for 0-RTT data must be compatible with those negotiated in the original connection's session ticket. If a server changes parameters that affect 0-RTT (e.g., flow control limits), the client must respect the remembered parameters or reject early data.
A QUIC endpoint MUST expand the payload of all UDP datagrams carrying Initial packets to at least 1200 bytes (using PADDING frames), preventing amplification attacks during the handshake and ensuring Version Negotiation responses are triggered.
A QUIC endpoint MUST NOT reuse a stream ID within a connection.
A receiver MUST close the connection with a FLOW_CONTROL_ERROR if the sender violates the advertised connection or stream data limits.
An endpoint MUST NOT send a Version Negotiation packet in response to receiving a Version Negotiation packet, preventing infinite loops.
An endpoint that receives a STOP_SENDING frame MUST send a RESET_STREAM frame if the stream is in the Ready or Send state, propagating the cancellation signal to the sender promptly.
Before a stream is created, all streams of the same type with lower-numbered stream IDs MUST be created, ensuring consistent stream creation order on both endpoints.
Connection IDs MUST NOT contain any information that an external observer can use to correlate them with other connection IDs for the same connection; the same connection ID MUST NOT be issued more than once on the same connection.
Each endpoint MUST include its initial Source Connection ID in the initial_source_connection_id transport parameter; the server MUST also echo the client's original Destination Connection ID in original_destination_connection_id, authenticating all handshake connection IDs.
Endpoints MUST explicitly negotiate an application protocol (via TLS ALPN) to avoid ambiguity about which application protocol is in use over the QUIC connection.
Senders MUST NOT send data in excess of either the stream-level or connection-level flow control limits set by the receiver.
The client's first Initial packet Destination Connection ID MUST be at least 8 bytes and chosen unpredictably; this value seeds the Initial packet protection keys and MUST remain constant until a server packet is received.
The cryptographic handshake MUST provide authenticated key exchange (server always authenticated, client optionally), authenticated exchange of transport parameters with confidentiality protection for server parameters, and authenticated negotiation of the application protocol.
The receiver MUST use the final size of a stream (carried in a STREAM+FIN frame or the Final Size field of RESET_STREAM) to account for all bytes sent on that stream in its connection-level flow controller. An endpoint MUST NOT send data at or beyond the final size.
Connection migration can allow observers to link pre- and post-migration traffic if the same connection ID is reused across network paths. Endpoints SHOULD retire connection IDs when they stop using a local or destination address and use fresh connection IDs on each new path to prevent correlation.
Each connection ID has an associated sequence number. Endpoints issue additional connection IDs via NEW_CONNECTION_ID frames (sequence numbers incrementing by 1) and retire obsolete ones via RETIRE_CONNECTION_ID frames. The active_connection_id_limit transport parameter caps how many active IDs a peer may maintain.
Multiple QUIC packets of different types (e.g., Initial, Handshake, 1-RTT) can be coalesced into a single UDP datagram. Receivers split datagrams by reading each packet's length field and process each QUIC packet independently, reducing round-trips during the handshake.
Stream IDs are 62-bit integers (0 to 2^62-1) encoded as variable-length integers. The least significant bit identifies the initiator (0=client, 1=server); the second least significant bit distinguishes bidirectional (0) from unidirectional (1) streams, yielding four stream types.
The stateless reset mechanism allows an endpoint that has lost connection state to terminate the connection. It sends a packet whose last 16 bytes are a Stateless Reset Token (derived from the connection ID via an HMAC-based construction). The peer identifies the token and closes the connection.
Transport parameters are exchanged inside the TLS handshake and thereby authenticated. They configure initial flow control limits (initial_max_data, initial_max_stream_data_*), stream concurrency limits (initial_max_streams_*), idle timeout, max UDP payload size, and connection ID constraints. Server parameters are TLS-encrypted; client parameters are not.
RFC 9000 establishes four IANA registries: QUIC Versions (4-byte values), QUIC Transport Parameters (variable-length integer codepoints), QUIC Frame Types (variable-length integer codepoints), and QUIC Transport Error Codes (variable-length integer codepoints). Provisional registrations require only a specification; permanent registrations require Standards Action or Expert Review depending on the codepoint range.
Despite encryption, QUIC packet timing, sizes, and direction are visible to on-path observers, enabling traffic analysis. The latency spin bit in 1-RTT packets intentionally exposes RTT. QUIC does not prevent traffic analysis; PADDING frames provide limited mitigation.
QUIC is susceptible to request forgery attacks where an adversary causes an endpoint to send crafted packets to third parties via malicious preferred addresses, spoofed migration, or Version Negotiation. Endpoints should validate and limit use of peer-supplied addresses.
QUIC restricts server responses to at most three times the data received from an unvalidated client address to prevent use as an amplification vector. Address validation via Retry packets or address tokens lifts this restriction.
The receiving part of a stream transitions through: Recv (initial) → Size Known (STREAM+FIN received) → Data Recvd (all data received) → Data Read (app consumed, terminal). A RESET_STREAM in Recv or Size Known moves to Reset Recvd → Reset Read (terminal).
The sending part of a stream transitions through: Ready (created) → Send (first STREAM/STREAM_DATA_BLOCKED sent) → Data Sent (FIN sent) → Data Recvd (all ACKed, terminal). An abort from any non-terminal state sends RESET_STREAM and moves through Reset Sent → Reset Recvd (terminal).
Long header packets carry a 1-byte header form/type, 4-byte Version, 1-byte DCIL + Destination Connection ID (0–20 bytes), 1-byte SCIL + Source Connection ID (0–20 bytes), and type-specific fields. Short header (1-RTT) packets omit the Version and Source Connection ID and include only the Destination Connection ID at an implicitly known length.
QUIC uses variable-length integer encoding (Section 16) where the two most significant bits of the first byte encode the byte length (00=1, 01=2, 10=4, 11=8 bytes), allowing values up to 2^62-1 and used throughout for stream IDs, offsets, and lengths.
The Version Negotiation packet has Header Form=1, Version=0x00000000, echoed Destination and Source Connection IDs from the triggering client packet, and one or more 4-byte Supported Version fields listing versions the server accepts.