--- /dev/null
+QUIC Connection State Machine
+=============================
+
+FSM Model
+---------
+
+QUIC client-side connection state can be broken down into five coarse phases of
+a QUIC connection:
+
+- The Idle substate (which is simply the state before we have started trying to
+ establish a connection);
+- The Active state, which comprises two substates:
+ - The Establishing state, which comprises many different substates;
+ - The Open state;
+- The Terminating state, which comprises several substates;
+- The Terminated state, which is the terminal state.
+
+There is monotonic progression through these phases.
+
+These names have been deliberately chosen to use different terminology to common
+QUIC terms such as 'handshake' to avoid confusion, as they are not the same
+concepts. For example, the Establishing state uses Initial, Handshake and 1-RTT
+packets.
+
+This discussion is (currently) given from the client side perspective only.
+State machine considerations only relevant to servers are not mentioned.
+0-RTT is also not currently modelled in this analysis.
+
+The synthesis of this FSM is not suggested by the QUIC RFCs but has been
+discerned from the requirements imposed. This does not mean that the
+implementation of this FSM as literally presented below is an optimal or
+advisable implementation strategy, and a cursory examination of existing QUIC
+implementations suggests that such an approach is not common. Moreover, excess
+attention should not be given to the Open state, as 1-RTT application
+communication can occur even still in the Establishing state (for example, when
+the handshake has been completed but not yet confirmed).
+
+However, the state machine described herein is helpful as an aid to
+understanding and broadly captures the logic which our implementation will
+embody. The design of the actual implementation is discussed further below.
+
+The above states and their substates are defined as follows:
+
+- The Establishing state involves the use of Initial and Handshake
+ packets. It is terminated when the handshake is confirmed.
+
+ Handshake confirmation is not the same as handshake completion.
+ Handshake confirmation occurs on the client when it receives
+ a `HANDSHAKE_DONE` frame (which occurs in a 1-RTT packet, thus
+ 1-RTT packets are also invoked in the Establishing state).
+ On the server, handshake confirmation occurs as soon as
+ the handshake is considered completed (see RFC 9001 s. 4.1).
+
+ The Establishing state is subdivided into the following substates:
+
+ - Proactive Version Negotiation (optional): The client sends
+ a Version Negotiation packet with a reserved version number
+ to forcibly elicit a list of the server's supported versions.
+ This is not expected to be commonly used, as it adds a round trip.
+
+ If it is used, the time spent in this state is based on waiting for
+ the server to respond, and potentially retransmitting after a
+ timeout.
+
+ - Pre-Initial: The client has completed proactive version negotiation
+ (if it performed it), but has not yet sent any encrypted packet. This
+ substate is included for exposition; no time will generally be spent in it
+ and there is immediate transmission of the first encrypted packet and
+ transition to Initial Exchange A.
+
+ - Initial Exchange A: The client has sent at least one Initial
+ packet to the server attempting to initiate a connection.
+
+ The client is waiting for a server response, which might
+ be:
+ - a Version Negotiation packet (leading to the Reactive Version
+ Negotiation state);
+ - a Retry packet (leading to Initial Exchange B); or
+ - an Initial packet (leading to the Initial Exchange Confirmed state).
+
+ - Reactive Version Negotiation: The server has rejected the client's
+ proposed version. If proactive version negotiation was used, this
+ can be considered an error. Otherwise, we return to the Pre-Initial
+ state and proceed as though proactive version negotiation was
+ performed using the information in the version negotiation packet.
+
+ - Initial Exchange B: The client has been asked to perform a Retry.
+ It sends at least one Initial packet to the server attempting to
+ initiate a connection. Every Initial packet contains the quoted Retry
+ Token. Any data sent in `CRYPTO` frames in Initial Exchange A must be
+ retransmitted, but PNs MUST NOT be reset. Note that this is still
+ considered part of the same connection, and QUIC Transport Parameters are
+ later used to cryptographically bind the established connection state to
+ the original DCIDs used as part of the Retry process. A server is not
+ allowed to respond to a Retry-triggered Initial exchange with another
+ Retry, and if it does we ignore it, which is the major distinction of this
+ state from Initial Exchange A.
+
+ The client is waiting for a server response, which might be:
+ - a Version Negotiation packet (invalid, ignored);
+ - a Retry packet (invalid, ignored);
+ - an Initial packet (leading to the Initial Exchange Continued
+ state);
+
+ - Initial Exchange Continued: The client has sent at least one
+ Initial packet to the server and received at least one valid Initial packet
+ from the server. There is no longer any possibility of a Retry (any such
+ packet is ignored) and communications may continue via Initial packets for
+ an arbitrarily long period until the handshake layer indicates the
+ Handshake EL is ready.
+
+ The client is waiting for server packets, until one of those packets
+ causes the handshake layer (whether it is TLS 1.3 or some other
+ hypothetical handshake layer) to emit keys for the Handshake EL.
+ This will generally occur due to incoming Initial packets containing
+ crypto stream segments (in the form of `CRYPTO` frames) which deliver
+ handshake layer protocol messages to the handshake layer in use.
+
+ - Handshake: The Handshake EL is now available to the client.
+ Either client or server may send the first Handshake packet.
+
+ The client is waiting to receive a Handshake packet from the server.
+
+ - Handshake Continued: The client has received and successfully
+ decrypted at least one Handshake packet. The client now discards
+ the Initial EL. Communications via the handshake EL may continue for
+ an arbitrary period of time.
+
+ The client is waiting to receive more Handshake packets from the
+ server to advance the handshake layer and cause it to transition
+ to the Handshake Completed state.
+
+ - Handshake Completed: The handshake layer has indicated that it
+ considers the handshake completed. For TLS 1.3, this means both
+ parties have sent and received (and verified) TLS 1.3 Finished
+ messages. The handshake layer must emit keys for the 1-RTT EL
+ at this time.
+
+ Though the handshake is not yet confirmed, the client can begin
+ sending 1-RTT packets.
+
+ The QUIC Transport Parameters sent by the peer are now authenticated.
+ (Though the peer's QUIC Transport Parameters may have been received
+ earlier in the handshake process, they are only considered
+ authenticated at this point.)
+
+ The client transitions to Handshake Confirmed once either
+ - it receives a `HANDSHAKE_DONE` frame in a 1-RTT packet, or
+ - it receives acknowledgement of any 1-RTT packet it sent.
+
+ Though this discussion only covers the client state machine, it is worth
+ noting that on the server, the handshake is considered confirmed as soon as
+ it is considered completed.
+
+ - Handshake Confirmed: The client has received confirmation from
+ the server that the handshake is confirmed.
+
+ The principal effect of moving to this state is that the Handshake
+ EL is discarded. Key Update is also now permitted for the first
+ time.
+
+ The Establishing state is now done and there is immediate transition
+ to the Open state.
+
+- The Open state is the steady state of the connection. It is a single state.
+
+ Application stream data is exchanged freely. Only 1-RTT packets are used. The
+ Initial, Handshake (and 0-RTT) ELs have been discarded, transport parameters
+ have been exchanged, and the handshake has been confirmed.
+
+ The client transitions to
+
+ - the Terminating — Closing state if the local application initiates an
+ immediate close (a `CONNECTION_CLOSE` frame is sent);
+ - the Terminating — Draining state if the remote peer initiates
+ an immediate close (i.e., a `CONNECTION_CLOSE` frame is received);
+ - the Terminated state if the idle timeout expires; a `CONNECTION_CLOSE`
+ frame is NOT sent;
+ - the Terminated state if the peer triggers a stateless reset; a
+ `CONNECTION_CLOSE` frame is NOT sent.
+
+- The Terminating state is used when closing the connection.
+ This may occur due to an application request or a transport-level
+ protocol error.
+
+ Key updates may not be initiated in the Terminating state.
+
+ This state is divided into two substates:
+
+ - The Closing state, used for a locally initiated immediate close. In
+ this state, a packet containing a `CONNECTION_CLOSE` frame is
+ transmitted again in response to any packets received. This ensures
+ that a `CONNECTION_CLOSE` frame is received by the peer even if the
+ initially transmitted `CONNECTION_CLOSE` frame was lost. Note that
+ these `CONNECTION_CLOSE` frames are not governed by QUIC's normal loss
+ detection mechanisms; this is a bespoke mechanism unique to this
+ state, which exists solely to ensure delivery of the `CONNECTION_CLOSE`
+ frame.
+
+ The endpoint progresses to the Terminated state after a timeout
+ interval, which should not be less than three times the PTO interval.
+
+ It is also possible for the endpoint to transition to the Draining
+ state instead, if it receives a `CONNECTION_CLOSE` frame prior
+ to the timeout expiring. This indicates that the peer is also
+ closing.
+
+ - The Draining state, used for a peer initiated immediate close.
+
+ The local endpoint may not send any packets of any kind in this
+ state. It may optionally send one `CONNECTION_CLOSE` frame immediately
+ prior to entering this state.
+
+ The endpoint progresses to the Terminated state after a timeout
+ interval, which should not be less than three times the PTO interval.
+
+- The Terminated state is the terminal state of a connection.
+ Regardless of how a connection ends (local or peer-initiated immediate close,
+ idle timeout, stateless reset), a connection always ultimately ends up in this
+ state. There is no longer any requirement to send or receive any packet. No
+ timer events related to the connection will ever need fire again. This is a
+ totally quiescent state. The state associated with the connection may now be
+ safely freed.
+
+We express this state machine in more concrete form in the form of a table,
+which makes the available transitions clear:
+
+† Except where superceded by a more specific transition
+
+ε means “where no other transition is applicable”.
+
+Where an action is specified in the Transition/Action column but no new state,
+no state change occurs.
+
+<table>
+<tr><th>State</th><th>Action On Entry/Exit</th><th>Event</th><th>Transition/Action</th></tr>
+<tr>
+ <td rowspan="2"><tt>IDLE</tt></td>
+ <td rowspan="2"></td>
+ <td>—<tt>APP:CONNECT</tt>→</td>
+ <td><tt>ACTIVE.ESTABLISHING.PROACTIVE_VER_NEG</tt> (if used), else
+ <tt>ACTIVE.ESTABLISHING.PRE_INITIAL</tt></td>
+</tr>
+<tr>
+ <td>—<tt>APP:CLOSE</tt>→</td>
+ <td><tt>TERMINATED</tt></td>
+</tr>
+<tr>
+ <td rowspan="5"><tt>ACTIVE</tt></td>
+ <td rowspan="5"></td>
+ <td>—<tt>IDLE_TIMEOUT</tt>→</td>
+ <td><tt>TERMINATED</tt></td>
+</tr>
+<tr>
+ <td>—<tt>PROBE_TIMEOUT</tt>→ †</td>
+ <td><tt>SendProbeIfAnySentPktsUnacked()</tt></td>
+</tr>
+<tr>
+ <td>—<tt>APP:CLOSE</tt>→ †</td>
+ <td><tt>TERMINATING.CLOSING</tt></td>
+</tr>
+<tr>
+ <td>—<tt>RX:ANY[CONNECTION_CLOSE]</tt>→</td>
+ <td><tt>TERMINATING.DRAINING</tt></td>
+</tr>
+<tr>
+ <td>—<tt>RX:STATELESS_RESET</tt>→</td>
+ <td><tt>TERMINATED</tt></td>
+</tr>
+
+<tr>
+ <td rowspan="3"><tt>ACTIVE.ESTABLISHING.PROACTIVE_VER_NEG</tt></td>
+ <td rowspan="3"><tt>enter:SendReqVerNeg</tt></td>
+ <td>—<tt>RX:VER_NEG</tt>→</td>
+ <td><tt>ACTIVE.ESTABLISHING.PRE_INITIAL</tt></td>
+</tr>
+<tr>
+ <td>—<tt>PROBE_TIMEOUT</tt>→</td>
+ <td><tt>ACTIVE.ESTABLISHING.PROACTIVE_VER_NEG</tt> (retransmit)</td>
+</tr>
+<tr>
+ <td>—<tt>APP:CLOSE</tt>→</td>
+ <td><tt>TERMINATED</tt></td>
+</tr>
+<tr>
+ <td rowspan="1"><tt>ACTIVE.ESTABLISHING.PRE_INITIAL</tt></td>
+ <td rowspan="1"></td>
+ <td>—ε→</td>
+ <td><tt>ACTIVE.ESTABLISHING.INITIAL_EXCHANGE_A</tt></td>
+</tr>
+<tr>
+ <td rowspan="4"><tt>ACTIVE.ESTABLISHING.INITIAL_EXCHANGE_A</tt></td>
+ <td rowspan="4"><tt>enter:SendPackets()</tt> (First Initial)</td>
+ <td>—<tt>RX:RETRY</tt>→</td>
+ <td><tt>ACTIVE.ESTABLISHING.INITIAL_EXCHANGE_B</tt></td>
+</tr>
+<tr>
+ <td>—<tt>RX:INITIAL</tt>→</td>
+ <td><tt>ACTIVE.ESTABLISHING.INITIAL_EXCHANGE_CONTINUED</tt></td>
+</tr>
+<tr>
+ <td>—<tt>RX:VER_NEG</tt>→</td>
+ <td><tt>ACTIVE.ESTABLISHING.REACTIVE_VER_NEG</tt></td>
+</tr>
+<tr>
+ <td>—<tt>CAN_SEND</tt>→</td>
+ <td><tt>SendPackets()</tt></td>
+</tr>
+<tr>
+ <td rowspan="1"><tt>ACTIVE.ESTABLISHING.REACTIVE_VER_NEG</tt></td>
+ <td rowspan="1"></td>
+ <td>—ε→</td>
+ <td><tt>ACTIVE.ESTABLISHING.PRE_INITIAL</tt></td>
+</tr>
+<tr>
+ <td rowspan="3"><tt>ACTIVE.ESTABLISHING.INITIAL_EXCHANGE_B</tt></td>
+ <td rowspan="3"><tt>enter:SendPackets()</tt><br/>
+ (First Initial, with token)<br/>
+ (*All further Initial packets contain the token)<br/>(*PN is not reset)</td>
+ <td>—<tt>RX:INITIAL</tt>→</td>
+ <td><tt>ACTIVE.ESTABLISHING.INITIAL_EXCHANGE_CONTINUED</tt></td>
+</tr>
+<tr>
+ <td>—<tt>PROBE_TIMEOUT</tt>→</td>
+ <td>TODO: Tail loss probe for initial packets?</td>
+</tr>
+<tr>
+ <td>—<tt>CAN_SEND</tt>→</td>
+ <td><tt>SendPackets()</tt></td>
+</tr>
+<tr>
+ <td rowspan="2"><tt>ACTIVE.ESTABLISHING.INITIAL_EXCHANGE_CONTINUED</tt></td>
+ <td rowspan="2"><tt>enter:SendPackets()</tt></td>
+ <td>—<tt>RX:INITIAL</tt>→</td>
+ <td>(packet processed, no change)</td>
+</tr>
+<tr>
+ <td>—<tt>TLS:HAVE_EL(HANDSHAKE)</tt>→</td>
+ <td><tt>ACTIVE.ESTABLISHING.HANDSHAKE</tt></td>
+</tr>
+<tr>
+ <td rowspan="3"><tt>ACTIVE.ESTABLISHING.HANDSHAKE</tt></td>
+ <td rowspan="3"><tt>enter:ProvisionEL(Handshake)</tt><br/>
+ <tt>enter:SendPackets()</tt> (First Handshake packet, if pending)</td>
+ <td>—<tt>RX:HANDSHAKE</tt>→</td>
+ <td><tt>ACTIVE.ESTABLISHING.HANDSHAKE_CONTINUED</tt></td>
+</tr>
+<tr>
+ <td>—<tt>RX:INITIAL</tt>→</td>
+ <td>(packet processed if EL is not dropped)</td>
+</tr>
+<tr>
+ <td>—<tt>CAN_SEND</tt>→</td>
+ <td><tt>SendPackets()</tt></td>
+</tr>
+<tr>
+ <td rowspan="3"><tt>ACTIVE.ESTABLISHING.HANDSHAKE_CONTINUED</tt></td>
+ <td rowspan="3"><tt>enter:DropEL(Initial)</tt><br/><tt>enter:SendPackets()</tt></td>
+ <td>—<tt>RX:HANDSHAKE</tt>→</td>
+ <td>(packet processed, no change)</td>
+</tr>
+<tr>
+ <td>—<tt>TLS:HANDSHAKE_COMPLETE</tt>→</td>
+ <td><tt>ACTIVE.ESTABLISHING.HANDSHAKE_COMPLETE</tt></td>
+</tr>
+ <tr>
+ <td>—<tt>CAN_SEND</tt>→</td>
+ <td><tt>SendPackets()</tt></td>
+</tr>
+<tr>
+ <td rowspan="3"><tt>ACTIVE.ESTABLISHING.HANDSHAKE_COMPLETED</tt></td>
+ <td rowspan="3"><tt>enter:ProvisionEL(1RTT)</tt><br/><tt>enter:HandshakeComplete()</tt><br/><tt>enter[server]:Send(HANDSHAKE_DONE)</tt><br/><tt>enter:SendPackets()</tt></td>
+ <td>—<tt>RX:1RTT[HANDSHAKE_DONE]</tt>→</td>
+ <td><tt>ACTIVE.ESTABLISHING.HANDSHAKE_CONFIRMED</tt></td>
+</tr>
+<tr>
+ <td>—<tt>RX:1RTT</tt>→</td>
+ <td>(packet processed, no change)</td>
+</tr>
+ <tr>
+ <td>—<tt>CAN_SEND</tt>→</td>
+ <td><tt>SendPackets()</tt></td>
+</tr>
+<tr>
+ <td rowspan="1"><tt>ACTIVE.ESTABLISHING.HANDSHAKE_CONFIRMED</tt></td>
+ <td rowspan="1"><tt>enter:DiscardEL(Handshake)</tt><br/><tt>enter:Permit1RTTKeyUpdate()</tt></td>
+ <td>—ε→</td>
+ <td><tt>ACTIVE.OPEN</tt></td>
+</tr>
+<tr>
+ <td rowspan="2"><tt>ACTIVE.OPEN</tt></td>
+ <td rowspan="2"></td>
+ <td>—<tt>RX:1RTT</tt>→</td>
+ <td>(packet processed, no change)</td>
+</tr>
+<tr>
+ <td>—<tt>CAN_SEND</tt>→</td>
+ <td><tt>SendPackets()</tt></td>
+</tr>
+<tr>
+ <td rowspan="2"><tt>TERMINATING</tt></td>
+ <td rowspan="2"></td>
+ <td>—<tt>TERMINATING_TIMEOUT</tt>→</td>
+ <td><tt>TERMINATED</tt></td>
+</tr>
+<tr>
+ <td>—<tt>RX:STATELESS_RESET</tt>→</td>
+ <td><tt>TERMINATED</tt></td>
+</tr>
+<tr>
+ <td rowspan="3"><tt>TERMINATING.CLOSING</tt></td>
+ <td rowspan="3"><tt>enter:QueueConnectionCloseFrame()</tt><br/><tt>enter:SendPackets()</tt></td>
+ <td>—<tt>RX:ANY[CONNECTION_CLOSE]</tt>→</td>
+ <td><tt>TERMINATING.DRAINING</tt></td>
+</tr>
+<tr>
+ <td>—<tt>RX:ANY</tt>→</td>
+ <td><tt>QueueConnectionCloseFrame()</tt><br/><tt>SendPackets()</tt></td>
+</tr>
+<tr>
+ <td>—<tt>CAN_SEND</tt>→</td>
+ <td><tt>SendPackets()</tt></td>
+</tr>
+<tr>
+ <td rowspan="1"><tt>TERMINATING.DRAINING</tt></td>
+ <td rowspan="1"></td>
+ <td></td>
+ <td></td>
+</tr>
+<tr>
+ <td rowspan="1"><tt>TERMINATED</tt></td>
+ <td rowspan="1"></td>
+ <td>[terminal state]</td>
+ <td></td>
+</tr>
+</table>
+
+Notes on various events:
+
+- `CAN_SEND` is raised when transmission of packets has been unblocked after previously
+ having been blocked. There are broadly two reasons why transmission of packets
+ may not have been possible:
+
+ - Due to OS buffers or network-side write BIOs being full;
+ - Due to limits imposed by the chosen congestion controller.
+
+ `CAN_SEND` is expected to be raised due to a timeout prescribed by the
+ congestion controller or in response to poll(2) or similar notifications, as
+ abstracted by the BIO system and how the application has chosen to notify
+ libssl of network I/O readiness.
+
+ It is generally implied that processing of a packet as mentioned above
+ may cause new packets to be queued and sent, so this is not listed
+ explicitly in the Transition column except for the `CAN_SEND` event.
+
+- `PROBE_TIMEOUT` is raised after the PTO interval and stimulates generation
+ of a tail loss probe.
+
+- `IDLE_TIMEOUT` is raised after the connection idle timeout expires.
+ Note that the loss detector only makes a determination of loss due to an
+ incoming ACK frame; if a peer becomes totally unresponsive, this is the only
+ mechanism available to terminate the connection (other than the local
+ application choosing to close it).
+
+- `RX:STATELESS_RESET` indicates receipt of a stateless reset, but note
+ that it is not guaranteed that we are able to recognise a stateless reset
+ that we receive, thus this event may not always be raised.
+
+- `RX:ANY[CONNECTION_CLOSE]` denotes a `CONNECTION_CLOSE` frame received
+ in any non-discarded EL.
+
+- Any circumstance where `RX:RETRY` or `RX:VER_NEG` are not explicitly
+ listed means that these packets are not allowed and will be ignored.
+
+- Protocol errors, etc. can be handled identically to `APP:CLOSE` events
+ as indicated in the above table if locally initiated. Protocol errors
+ signalled by the peer are handled as `RX:ANY[CONNECTION_CLOSE]` events.
+
+Notes on various actions:
+
+- `SendPackets()` sends packets if we have anything pending for transmission,
+ and only to the extent we are able to with regards to congestion control and
+ available BIO buffer space, etc.
+
+Non-FSM Model
+-------------
+
+Common QUIC implementations appear to prefer modelling connection state as a set
+of flags rather than as a FSM. It can be observed above that there is a fair
+degree of commonality between many states. This has been modelled above using
+hierarchical states with default handlers for common events. [The state machine
+can be viewed as a diagram here (large
+image).](./images/connection-state-machine.png)
+
+We transpose the above table to sort by events rather than states, to discern
+the following list of events:
+
+- `APP:CONNECT`: Supported in `IDLE` state only.
+
+- `RX:VER_NEG`: Handled in `ESTABLISHING.PROACTIVE_VER_NEG` and
+ `ESTABLISHING.INITIAL_EXCHANGE_A` only, otherwise ignored.
+
+- `RX:RETRY`: Handled in `ESTABLISHING.INITIAL_EXCHANGE_A` only.
+
+- `PROBE_TIMEOUT`: Applicable to `OPEN` and all (non-ε) `ESTABLISHING`
+ substates. Handled via `SendProbeIfAnySentPktsUnacked()` except in the
+ `ESTABLISHING.PROACTIVE_VER_NEG` state, which reenters that state to trigger
+ retransmission of a Version Negotiation packet.
+
+- `IDLE_TIMEOUT`: Applicable to `OPEN` and all (non-ε) `ESTABLISHING` substates.
+ Action: immediate transition to `TERMINATED` (no `CONNECTION_CLOSE` frame
+ is sent).
+
+- `TERMINATING_TIMEOUT`: Timeout used by the `TERMINATING` state only.
+
+- `CAN_SEND`: Applicable to `OPEN` and all (non-ε) `ESTABLISHING`
+ substates, as well as `TERMINATING.CLOSING`.
+ Action: `SendPackets()`.
+
+- `RX:STATELESS_RESET`: Applicable to all `ESTABLISHING` and `OPEN` states and
+ the `TERMINATING.CLOSING` substate.
+ Always causes a direct transition to `TERMINATED`.
+
+- `APP:CLOSE`: Supported in `IDLE`, `ESTABLISHING` and `OPEN` states.
+ (Reasonably a no-op in `TERMINATING` or `TERMINATED.`)
+
+- `RX:ANY[CONNECTION_CLOSE]`: Supported in all `ESTABLISHING` and `OPEN` states,
+ as well as in `TERMINATING.CLOSING`. Transition to `TERMINATING.DRAINING`.
+
+- `RX:INITIAL`, `RX:HANDSHAKE`, `RX:1RTT`: Our willingness to process these is
+ modelled on whether we have an EL provisioned or discarded, etc.; thus
+ this does not require modelling as additional state.
+
+ Once we successfully decrypt a Handshake packet, we stop processing Initial
+ packets and discard the Initial EL, as required by RFC.
+
+- `TLS:HAVE_EL(HANDSHAKE)`: Emitted by the handshake layer when Handshake EL
+ keys are available.
+
+- `TLS:HANDSHAKE_COMPLETE`: Emitted by the handshake layer when the handshake
+ is complete. Implies connection has been authenticated. Also implies 1-RTT EL
+ keys are available. Whether the handshake is complete, and also whether it is
+ confirmed, is reasonably implemented as a flag.
+
+From here we can discern state dependence of different events:
+
+ - `APP:CONNECT`: Need to know if application has invoked this event yet,
+ as if so it is invalid.
+
+ State: Boolean: Connection initiated?
+
+ - `RX:VER_NEG`: Only valid if we have not yet received any successfully
+ processed encrypted packet from the server.
+
+ - `RX:RETRY`: Only valid if we have sent an Initial packet to the server,
+ have not yet received any successfully processed encrypted packet
+ from the server, and have not previously been asked to do a Retry as
+ part of this connection (and the Retry Integrity Token validates).
+
+ Action: Note that we are now acting on a retry and start again.
+ Do not reset packet numbers. The original CIDs used for the first
+ connection attempt must be noted for later authentication in
+ the QUIC Transport Parameters.
+
+ State: Boolean: Retry requested?
+
+ State: CID: Original SCID, DCID.
+
+ - `PROBE_TIMEOUT`: If we have sent at least one encrypted packet yet,
+ we can handle this via a standard probe-sending mechanism. Otherwise, we are
+ still in Proactive Version Negotiation and should retransmit the Version
+ Negotiation packet we sent.
+
+ State: Boolean: Doing proactive version negotiation?
+
+ - `IDLE_TIMEOUT`: Only applicable in `ACTIVE` states.
+
+ We are `ACTIVE` if a connection has been initiated (see `APP:CONNECT`) and
+ we are not in `TERMINATING` or `TERMINATED`.
+
+ - `TERMINATING_TIMEOUT`: Timer used in `TERMINATING` state only.
+
+ - `CAN_SEND`: Stimulates transmission of packets.
+
+ - `RX:STATELESS_RESET`: Always handled unless we are in `TERMINATED`.
+
+ - `APP:CLOSE`: Usually causes a transition to `TERMINATING.CLOSING`.
+
+ - `RX:INITIAL`, `RX:HANDSHAKE`, `RX:1RTT`: Willingness to process
+ these is implicit in whether we currently have the applicable EL
+ provisioned.
+
+ - `TLS:HAVE_EL(HANDSHAKE)`: Handled by the handshake layer
+ and forwarded to the record layer to provision keys.
+
+ - `TLS:HANDSHAKE_COMPLETE`: Should be noted as a flag and notification
+ provided to various components.
+
+We choose to model the CSM's state as follows:
+
+ - The `IDLE`, `ACTIVE`, `TERMINATING.CLOSED`, `TERMINATING.DRAINED` and
+ `TERMINATED` states are modelled explicitly as a state variable. However,
+ the substates of `ACTIVE` are not explicitly modelled.
+
+ - The following flags are modelled:
+ - Retry Requested? (+ Original SCID, DCID if so)
+ - Have Sent Any Packet?
+ - Are we currently doing proactive version negotiation?
+ - Have Successfully Received Any Encrypted Packet?
+ - Handshake Completed?
+ - Handshake Confirmed?
+
+ - The following timers are modelled:
+ - PTO Timeout
+ - Terminating Timeout
+ - Idle Timeout
+
+Implementation Plan
+-------------------
+
+- Phase 1: “Steady state only” model which jumps to the `ACTIVE.OPEN`
+ state with a hardcoded key.
+
+ Test plan: Currently uncertain, to be determined.
+
+- Phase 2: “Dummy handshake” model which uses a one-byte protocol
+ as the handshake layer as a standin for TLS 1.3. e.g. a 0x01 byte “represents”
+ a ClientHello, a 0x02 byte “represents” a ServerHello. Keys are fixed.
+
+ Test plan: If feasible, an existing QUIC implementation will be modified to
+ use this protocol and E2E testing will be performed against it. (This
+ can probably be done quickly but an alternate plan may be required if
+ the effort needed turns out be excessive.)
+
+- Phase 3: Final model with TLS 1.3 handshake layer fully plumbed in.
+
+ Test plan: Testing against real world implementations.