aboutsummaryrefslogtreecommitdiff
path: root/proposals
diff options
context:
space:
mode:
Diffstat (limited to 'proposals')
-rw-r--r--proposals/000-index.txt2
-rw-r--r--proposals/339-udp-over-tor.md2
-rw-r--r--proposals/340-packed-and-fragmented.md111
-rw-r--r--proposals/346-protovers-again.md5
-rw-r--r--proposals/349-command-state-validation.md666
-rw-r--r--proposals/BY_INDEX.md1
-rw-r--r--proposals/BY_STATUS.md1
-rw-r--r--proposals/SUMMARY.md1
8 files changed, 753 insertions, 36 deletions
diff --git a/proposals/000-index.txt b/proposals/000-index.txt
index 8c9668f..7e37484 100644
--- a/proposals/000-index.txt
+++ b/proposals/000-index.txt
@@ -270,6 +270,7 @@ Proposals by number:
346 Clarifying and extending the use of protocol versioning [OPEN]
347 Domain separation for certificate signing keys [OPEN]
348 UDP Application Support in Tor [OPEN]
+349 Client-Side Command Acceptance Validation [DRAFT]
Proposals by status:
@@ -279,6 +280,7 @@ Proposals by status:
316 FlashFlow: A Secure Speed Test for Tor (Parent Proposal)
331 Res tokens: Anonymous Credentials for Onion Service DoS Resilience
342 Decoupling hs_interval and SRV lifetime
+ 349 Client-Side Command Acceptance Validation
NEEDS-REVISION:
212 Increase Acceptable Consensus Age [for 0.2.4.x+]
219 Support for full DNS and DNSSEC resolution in Tor [for 0.2.5.x]
diff --git a/proposals/339-udp-over-tor.md b/proposals/339-udp-over-tor.md
index 12de0c6..e5235d1 100644
--- a/proposals/339-udp-over-tor.md
+++ b/proposals/339-udp-over-tor.md
@@ -11,7 +11,7 @@ Status: Accepted
Tor currently only supports delivering two kinds of traffic to the
internet: TCP data streams, and a certain limited subset of DNS
requests. This proposal describes a plan to extend the Tor protocol so
-that exit relays can also relay UDP traffic to the network?.
+that exit relays can also relay UDP traffic to the network.
Why would we want to do this? There are important protocols that use
UDP, and in order to support users that rely on these protocols, we'll
diff --git a/proposals/340-packed-and-fragmented.md b/proposals/340-packed-and-fragmented.md
index da0cbdb..2407f99 100644
--- a/proposals/340-packed-and-fragmented.md
+++ b/proposals/340-packed-and-fragmented.md
@@ -22,6 +22,9 @@ between messages and cells, for two reasons:
(notably `SENDME`, `XON`, `XOFF`, and several types from
[proposal 329](./329-traffic-splitting.txt)) are much smaller than
the relay cell size, and could be sent comparatively often.
+ We also want to be able to *hide* the transmission of small control messages
+ by packing them into what would have been the padding of other messages,
+ making them effectively invisible to a network traffic observer.
In this proposal, we describe a way to decouple relay cells from relay
messages. Relay messages can now be packed into multiple cells or split
@@ -65,7 +68,7 @@ If we are lucky, we won't have to build this encryption at all, and we
can just move to some version of GCM-UIV or other RPRP that reserves 16
bytes for an authentication tag or similar cryptographic object.
-The `body` MUST contain exactly 493 bytes as cells have a fixed size.
+The `body` MUST contain exactly 493 bytes as relay cells have a fixed size.
## New relay message packing
@@ -94,14 +97,16 @@ The following message types take required stream IDs: `BEGIN`, `DATA`, `END`,
The following message types from proposal 339 (UDP) take required stream IDs:
`CONNECT_UDP`, `CONNECTED_UDP` and `DATAGRAM`.
-No other message types take stream IDs. The `stream_id` field, when present,
-MUST NOT be zero.
+No other current message types take stream IDs. The `stream_id` field, when
+present, MUST NOT be zero.
Messages can be split across relay cells; multiple messages can occur in
a single relay cell. We enforce the following rules:
* Headers may not be split across cells.
* If a 0 byte follows a message body, there are no more messages.
+ * A message body is permitted to end at exactly the end of a relay cell,
+ without a 0 byte afterwards.
* A relay cell may not be "empty": it must have at least some part of
some message.
@@ -116,40 +121,41 @@ Receivers MUST validate that the cell `header` and the `message header` are
well-formed and have valid lengths while handling the cell in which the header
is encoded. If any of them is invalid, the circuit MUST be destroyed.
-An unrecognized `command` is considered invalid and thus MUST result in the
-circuit being immediately destroyed.
+A message header with an unrecognized `command` is considered invalid and thus
+MUST result in the circuit being immediately destroyed (without waiting for the
+rest of the relay message to arrive, in the case of a fragmented message).
-## Negotiation (OBSOLETE)
+## New subprotocol `RelayCell`
-> We do not want to do it this way. Instead, we want to use the system
-> described in the forthcoming proposal, "xxx-protovers-again.md".
+We introduce a new subprotocol `RelayCell` to specify the relay cell ABI. The
+new format specified in this proposal, supporting packing and fragmentation,
+corresponds to `RelayCell` version 1. The ABI prior to this proposal is
+`RelayCell` version 0.
-This message format requires a new `Relay` subprotocol version to indicate
-support. If a client wants to indicate support for this format, it sends the
-following extension as part of its `ntor3` handshake:
+All clients and relays implicitly support `RelayCell` version 0.
- EXT_FIELD_TYPE:
+> XXX: Do we want to consider some migration path for eventually removing
+> support for `RelayCell` version 0? e.g. maybe this should be something like
+> "Support for any of `Relay` versions 1-5 imply support for `RelayCell`
+> version 0"?
- [03] -- Packed and Fragmented Cell Request
+We reserve the protocol ID 13 for binary encoding of this subprotocol with
+respect to [proposal 346][prop346] and [proposal 323][prop323].
-This field is zero payload length. Its presence signifies that the client
-wants to use packed and fragmented cells on the circuit.
+To use `RelayCell` version 1 or greater with a given relay on a given circuit,
+the client negotiates it using an `ntor_v3` extension, as per [proposal
+346][prop346]. This implies that the relay must advertise support for `Relay`
+version 5 (`ntor_v3` circuit extensions) as well as the target `RelayCell`
+version (1 for the format introduced in this proposal).
-The Exit side ntorv3 response payload is encoded as:
-
- EXT_FIELD_TYPE:
-
- [04] -- Packed and Fragmented Cell Response
-
-Again, the extension presence indicates to the client that the Exit has
-acknowledged the feature and is ready to use it. If the extension is not
-present, the client MUST not use the packed and fragmented feature even though
-the Exit has advertised the correct protover.
-
-The client MUST reject the handshake and thus close the circuit if:
-
- - The response extension is seen for a non-ntorv3 handshake.
- - The response extension is seen but no request was made initially.
+Circuits using mixed `RelayCell` versions are permitted. e.g. we anticipate
+some of the use-cases for packing and fragmentation to only need the exit-relay
+to support it. Not requiring `RelayCell=1` for other relays in the circuit
+provides a larger pool of candidate relays. While an intermediate relay using a
+different `RelayCell` version than the destination relay of a given relay cell
+will look at the wrong bytes for the `recognized` and `digest` fields, they
+will reach the correct conclusion that the cell is not intended for them and
+pass it to the next hop in the circuit.
## Migration
@@ -181,6 +187,23 @@ there is, then it adds a DATA message to the end of the current cell,
with as much data as possible. Otherwise, the client sends the cell
with no packed data.
+> XXX: This isn't quite right. What was actually implemented in tor, and
+> what we want in arti, is to defer sending some "control" messages like
+> confluence switch and (non-first) xon, until they can be invisibly packed
+> into a cell for a DATA message.
+>
+> dgoulet: Could you update this section with the concrete details, and exactly
+> what property we're trying to achieve? e.g.:
+>
+> If we have data to send, but the corresponding DATA messages don't leave
+> enough room to pack in the deferred control message(s), what do we do? If we
+> continue deferring could we end up deferring forever if the application
+> always writes in chunks that happen to align this way?
+>
+> Since cells containing any part of a DATA message is subject to congestion
+> windows, does that mean if our congestion window is empty we can't send these
+> control messages either (until the window becomes non-empty)?
+
## Onion services
Negotiating this for onion services will happen in a separate proposal;
@@ -259,7 +282,7 @@ follows:
accommodates most reasonable MTU choices)
Any increase in maximum length for any other message type requires a new
-Relay subprotocol version. (For example, if we later want to allow
+`RelayCell` subprotocol version. (For example, if we later want to allow
EXTEND2 messages to be 2000 bytes long, we need to add a new proposal
saying so, and reserving a new subprotocol version.)
@@ -288,7 +311,27 @@ Here is an example of the simplest case: one message, sent in one relay cell:
random [464 bytes]
```
-Total of 514 bytes which is the absolute maximum cell size.
+Total of 514 bytes which is the absolute maximum relay cell size.
+
+A message whose body ends at exactly the end of a relay cell has no
+corresponding end-of-messages marker.
+
+```
+ Cell 1:
+ header:
+ circid .. [4 bytes]
+ command RELAY [1 byte]
+ relay cell header:
+ recognized 0 [2 bytes]
+ digest (...) [14 bytes]
+ message header:
+ command DATA [1 byte]
+ length 488 [2 bytes]
+ message routing header:
+ stream_id 42 [2 bytes]
+ message body:
+ (data) [488 bytes]
+```
Here's an example with fragmentation only: a large EXTEND2 message split
across two relay cells.
@@ -320,7 +363,6 @@ across two relay cells.
0 [1 byte]
padding up to end of cell:
random [182 bytes]
-
```
Each cells are 514 bytes for a message body totalling 800 bytes.
@@ -428,3 +470,6 @@ what parties need to accept.)
padding up to end of cell:
random [241 bytes]
```
+
+[prop323]: ./323-walking-onions-full.md "Specification for Walking Onions"
+[prop346]: ./346-protovers-again.md "Clarifying and extending the use of protocol versioning"
diff --git a/proposals/346-protovers-again.md b/proposals/346-protovers-again.md
index a18e238..a5c7e24 100644
--- a/proposals/346-protovers-again.md
+++ b/proposals/346-protovers-again.md
@@ -93,7 +93,8 @@ ordered in the same way as in tor-spec.
| Padding | 10 |
| FlowCtrl | 11 |
| Conflux | 12 |
-| Datagram | 13 |
+| RelayCell| 13 |
+| Datagram | TBD|
> Note: This is the same encoding used in [walking onions proposal][prop323].
> It takes its order from the ordering of protocol versions in
@@ -172,7 +173,7 @@ Currently specified subprotocol versions which can be negotiated using
this extension are:
* FlowCtrl=2 (congestion control)
- * Packed-and-fragmented support (proposal 340)
+ * RelayCell=1 (proposal 340)
The availability of the `subproto_request` extension itself
will be indicated by a new Relay=X flag. When used, it will supplant
diff --git a/proposals/349-command-state-validation.md b/proposals/349-command-state-validation.md
new file mode 100644
index 0000000..47598e6
--- /dev/null
+++ b/proposals/349-command-state-validation.md
@@ -0,0 +1,666 @@
+```
+Filename: 349-command-state-validation.md
+Title: Client-Side Command Acceptance Validation
+Author: Mike Perry
+Created: 2023-08-17
+Status: Draft
+```
+
+# Introduction
+
+The ability of relays to inject end-to-end relay cells that are ignored by
+clients allows malicious relays to create a covert channel to verify that they
+are present in multiple positions of a path. This covert channel allows a
+Guard to deanonymize 100% of its traffic, or just all the traffic of a
+particular client IP address.
+
+This attack was first documented in [DROPMARK]. Proposal 344 describes the
+severity of this attack, and how this kind of end-to-end covert channel leads
+to full deanonymization, in a reliable way, in practice. (Recall that dropped
+cell attacks are most severe when an adversary can inject arbitrary end-to-end
+data patterns at times when the circuit is known to be idle, before it is used
+for traffic; injection at this point enables path bias attacks which can
+ensure that only malicious Guard+Exit relays are present in all circuits used
+by a particular target client IP address. For further details, see Proposal
+344.)
+
+This proposal is targeting arti-client, not C-Tor. This proposal is specific
+to client-side checks of relay cells and relay messages. Its primary change to
+behavior is the definition of state machines that enforce what relay message
+commands are acceptable on a given circuit, and when.
+
+By applying and enforcing these state machine rules, we prevent the end-to-end
+transmission of arbitrary amounts of data, and ensure that predictable periods
+of the protocol are happening as expected, and not filled with side channel
+packet patterns.
+
+
+
+## Overview of dropped cell types
+
+Dropped cells are cells that a relay can inject that end up ignored and
+discarded by a Tor client.
+
+These include:
+ 1. Unparsable cells
+ 2. invalid relay commands
+ 3. Unrecognized cells (ie: wrong source hop, or decrypt failures)
+ 4. unsupported (or consensus-disabled) relay commands or extensions
+ 5. out-of-context relay commands
+ 6. duplicate relay commands
+ 7. relay commands that hit any error codepaths
+ 8. relay commands for an invalid or already-closed stream ID
+ 9. semantically void relay cells (incl relay data len == 0, or PING)
+ 10. onion descriptor-appended junk
+
+Items 1-4 and 8 are handled by the existing relay command parsers in arti. In
+these cases, arti closes the circuit already.
+
+> XXX: Arti's relay parser is lazy; see https://gitlab.torproject.org/tpo/core/arti/-/merge_requests/1978
+> Does this mean that individual components need to properly propagate error
+> information in order for circuits to get closed, when a command does not
+> parse?
+
+The state machines of this proposal handle 5-7 in a rigorous way. (In many
+cases of out-of-context relay cells, arti already closes the circuit;
+our goal here is to centralize this validation so that we can ensure that
+it is not possible for any relay commands to omit checks or allow unbounded
+activity.)
+
+> XXX: Does arti allow extra onion-descriptor junk to be appended after the
+> descriptor signature? C-Tor does...
+
+
+# Architectural Patterns and Behavior
+
+Ideally, the handling of invalid protocol behavior should be centralized,
+so that validation can happen in one easy-to-audit place, rather than spread
+across the codebase (as it currently is with C-Tor).
+
+For some narrow cases of invalid protocol activity, this is trivial. The relay
+command acceptance is centralized in arti, which allows arti to immediately
+reject unknown or disabled relay commands. This kind of validation is
+necessary, but not sufficient, in order to prevent dropped cell vectors.
+
+Things quickly get complicated when handling parsable relay cells sent during
+an inappropriate time, or other activity such as duplicate relay commands,
+semantically void cells, or commands that would hit an error condition, or
+lazy parsing failure, deep in the code and be silently accepted without
+closing the circuit.
+
+To handle such cases, we propose adding a relay command message state machine
+pattern. Each relay protocol, when it becomes active on a circuit, must
+register a state machine that handles validating its messages.
+
+Because multiple relay protocols can be active at a time, multiple validation
+state machines can be attached to a circuit. This also allows protocols to
+create their own validation without needing to modify the entire validation
+process. Relay messages that are not accepted by any active protocol
+validation handler MUST result in circuit close.
+
+
+## Architectural Patterns
+
+In order to handle these cases, we rely on some architectural patterns:
+ 1. No relay message command may be sent to the client unless it is unless
+ explicitly allowed by the specification, advertised as supported, and
+ negotiated on a particular channel or circuit. (Prop#346)
+ 2. Any relay commands or extension fields not successfully negotiated
+ on a circuit are invalid. This includes cells from intermediate hops,
+ which must also negotiate their use (example: padding machine
+ negotiation to middles).
+ 3. By following the above principles, state machines can be developed
+ that govern when a relay command is acceptable. This covers the
+ majority of protocol activity. See Section 3.
+ 4. For some commands, additional checks must be performed by using
+ context of the protocol itself.
+
+The following relay commands require additional module state to enforce
+limitations, beyond what is known by a state machine, for #4:
+ - RELAY_COMMAND_SENDME
+ - Requires checking that the auth digest hash is accurate
+ - RELAY_COMMAND_XOFF and RELAY_COMMAND_XON
+ - Context and rate limiting is stream-dependent
+ - Packing enforcement via prop#340 is context-dependent
+ - RELAY_COMMAND_CONFLUX_SWITCH
+ - Packing enforcement via prop#340 is context-dependent
+ - RELAY_COMMAND_DROP:
+ - This can only be accepted from a hop if there is a padding
+ machine at that hop.
+ - RELAY_COMMAND_INTRODUCE2
+ - Requires inspecting replay cache (however, circuits should not get
+ closed because replays can come from the client)
+
+## Behavior
+
+When an invalid relay cell or relay message is encountered, the corresponding
+circuit should be immediately closed.
+
+Initially, this can be accomplished by sending a DESTROY cell to the Guard
+relay.
+
+Additionally, when closing circuits in this way, clients must take care not to
+allow cases of adversarially-induced infinite circuit creation in non-onion
+service protocols that are not protected by Vanguards/Vanguards-lite, by
+limiting the number of retries they perform. (One such example of this is a
+malicious conflux exit that repeatedly kills only one leg by injecting dropped
+cells to close the circuit.)
+
+While we also specify some cases where the channel to the Guard should be
+closed, this is not necessary in the general case.
+
+> XXX: I can't think of any issues severe enough to actually warrant the
+> following, but Florentin pointed it out as a possibility: A malicious Guard
+> may withhold the DESTROY, and still allow full identifier transmission before
+> the circuit is closed. While this does not directly allow full deanonymization
+> because the client won't actually use the circuit, it may still be enough to
+> make the vector useful for other attacks. For completeness against this
+> vector, we may want to consider sending a new RELAY_DESTROY command to the
+> middle node, such that it has responsibility for tearing down a circuit by
+> sending its own DESTROYS in both directions, and then have the client send its
+> own DESTROY if the client does not get a DESTROY from the Guard.
+> >>> See torspec#220: https://gitlab.torproject.org/tpo/core/torspec/-/issues/220
+
+
+
+# State machine descriptions
+
+These state machines apply only at the client. (There is no information leak
+from extra cells in the protocol on the relay side, so we will not be specifying
+relay-side enforcement, or implementing it for C-Tor.)
+
+There are multiple state machines, describing particular circuit purposes
+and/or components of the Tor relay protocol.
+
+Each state machine has a "Trigger", and a "Message Scope". The "Trigger" is
+the condition, relay command, or action that causes the state machine to get
+added to a circuit's command state validator set. The Message Scope is where the state
+machine applies: to specific a hop number, stream ID, or both.
+
+A circuit can have multiple state machines attached at one time.
+ * If no state machine accepts a relay command, then the circuit MUST be
+ closed.
+ * When we say "receive X" we mean "receive a _valid_ cell of
+ type X". If the cell is invalid, we MUST kill the circuit
+
+## Relay message handlers
+
+The state machines process enveloped relay message commands. Ie, with respect
+to prop#340, they operate on the message bodies, with associated stream ID.
+
+With respect to Proposal #340, the calls to state machine validation would go
+after converting cells to messages, but before parsing the message body
+itself, to still minimize exposure of the parser attack surfaces.
+
+> XXX: Again, some validation will require early parsing, not lazy parsing
+
+There are multiple relay message handlers that can be registered with each
+circuit ID, for a specific hop on that circuit ID, depending on the protocols
+that are in use on that circuit with that hop, as well as the streams to that
+hop.
+
+Each handler has a Message Scope, that acts as a filter such that only relay
+command messages from this scope are processed by that handler.
+
+If a message is not accepted by any active handler, the circuit MUST be
+closed.
+
+
+### Base Handler
+
+Purpose: This handler validates commands for circuit construction and
+circuit-level SENDME activity.
+
+Trigger: Creation of a circuit; ntor handhshake success for a hop
+
+Message Scope: The circuit ID and hop number must match for this handler to
+apply. (Because of leaky pipes, each hop of the circuit has a base handler
+added when that hop completes an ntor handshake and is added to the circuit.)
+
+```text
+START:
+ Upon sending EXTEND:
+ Enter EXTEND_SENT.
+
+ Receive SENDME:
+ Ensure expected auth digest matches; close circuit otherwise
+ No transition.
+
+EXTEND_SENT:
+ Receiving EXTENDED:
+ Enter START.
+
+ Receive SENDME:
+ Ensure expected auth digest matches; close circuit otherwise
+ No transition.
+```
+
+### Client Introducing Handler
+
+Purpose: Circuits used by clients to connect to a service introduction point
+have this handler attached.
+
+Trigger: Usage of a circuit for client introduction
+
+Message Scope: Circuit ID and hop number must match
+
+```text
+CLIENT_INTRO_START:
+ Upon sending INTRODUCE1:
+ Enter CLIENT_INTRO_WAIT
+
+CLIENT_INTRO_WAIT
+ Receieve INTRODUCE_ACK:
+ Accept
+ Transition to CLIENT_INTRO_END
+
+CLIENT_INTRO_END:
+ No transitions possible
+ - XXX: Enforce that no new handlers can be added? We may still have padding
+ handlers though.
+```
+
+
+### Service Introduce Handler
+
+Purpose: Service-side onion service introduction circuits have this handler
+attached.
+
+Trigger: Onion service establishing an introduction point circuit
+
+Message Scope: Circuit ID and hop number must match
+
+```text
+SERVICE_INTRO_START:
+ Upon sending ESTABLISH_INTRO:
+ Enter SERVICE_INTRO_ESTABLISH
+
+SERVICE_INTRO_ESTABLISH:
+ Receiving INTRO_ESTABLISHED:
+ Enter SERVICE_INTRO_ESTABLISHED
+
+SERVICE_INTRO_ESTABLISHED:
+ Receiving INTRODUCE2
+ Accept
+```
+
+
+### Client Rendezvous Handler
+
+Purpose: Circuits used by clients to build a rendezvous point have this handler
+attached.
+
+Trigger: Client rendezvous initiation
+
+Message Scope: Circuit ID and hop number must match
+
+```text
+CLIENT_REND_START:
+ Upon Sending RENDEZVOUS1:
+ Enter CLIENT_REND_WAIT
+
+CLIENT_REND_WAIT:
+ Receive RENDEZVOUS2:
+ Enter CLIENT_REND_ESTABLISHED
+
+CLIENT_REND_ESTABLISHED:
+ Remain in this state; launch TCP, UDP, or Conflux handlers for streams
+```
+
+
+### Service Rendezvous Handler
+
+Purpose: Circuits used by services to connect to a rendezvous point have this
+handler attached.
+
+Trigger: Incoming introduce cell/service rend initiation
+
+Message Scope: Circuit ID and hop number must match
+
+```text
+SERVICE_REND_START:
+ Upon sending ESTABLISH_RENDEZVOUS:
+ Enter SERVICE_REND_WAIT
+
+SERVICE_REND_WAIT:
+ Receive RENDEZVOUS_ESTABLISHED:
+ Enter SERVICE_REND_ESTABLISHED
+
+SERVICE_REND_ESTABLISHED:
+ Remain in this state; launch TCP, UDP, or Conflux handlers for streams
+```
+
+
+### CircPad Handler
+
+Purpose: Circuit-level padding is negotiated with a particular hop in the
+circuit; when it is negotiated, we need to allow padding cells from that hop.
+
+Trigger: Negotiation of a circuit padding machine
+
+Message Scope: Circuit ID and hop must match; padding machine must be active
+
+```text
+PADDING_START:
+ Upon sending PADDING_NEGOTIATE:
+ Enter PADDING_NEGOTIATING
+
+PADDING_NEGOTIATING:
+ Receiving PADDING_NEGOTIATED:
+ Enter PADDING_ACTIVE
+
+PADDING_ACTIVE:
+ Receiving DROP:
+ Accept (if from correct hop)
+ - XXX: We could perform more sophisticated rate limiting accounting here
+ too?
+```
+
+### Resolve Stream Handler
+
+Purpose: This handler is created on circuits when a resolve happens.
+
+Trigger: RESOLVE message
+
+Message Scope: Circuit ID, stream ID, and hop number must all match
+
+```text
+RESOLVE_START:
+ Send a RESOLVE message:
+ Enter RESOLVE_SENT
+
+RESOLVE_SENT:
+ Receive a RESOLVED or an END:
+ Enter RESOLVE_START.
+```
+
+
+### TCP Stream handler
+
+Purpose: This handler is created when the client creates a new stream ID, using either
+BEGIN or BEGIN_DIR.
+
+Trigger: New AP or DirConn stream
+
+Message Scope: Circuit ID, stream ID, and hop number must all match; stream ID
+must be open or half-open (half-open is END_SENT).
+
+```text
+TCP_STREAM_START:
+ Send a BEGIN or BEGIN_DIR message:
+ Enter BEGIN_SENT.
+
+BEGIN_SENT:
+ Receive an END:
+ Enter TCP_STREAM_START.
+ Receive a CONNECTED:
+ Enter STREAM_OPEN.
+
+STREAM_OPEN:
+ Receive DATA:
+ Verify length is > 0
+ XXX: Handle [HSDIRINFLATION] here?
+ Process.
+
+ Receive XOFF:
+ Enter STREAM_XOFF
+
+ Send END:
+ Enter END_SENT.
+
+ Receive END:
+ Enter TCP_STREAM_START
+
+STREAM_XOFF:
+ Receive DATA:
+ Verify length is > 0
+ XXX: Handle [HSDIRINFLATION] here?
+ Process.
+
+ Send END:
+ Enter END_SENT.
+
+ Receive XON:
+ Enter STREAM_XON
+
+ Receive END:
+ Enter TCP_STREAM_START
+
+STREAM_XON:
+ Receive DATA:
+ Verify length is > 0
+ XXX: Handle [HSDIRINFLATION] here?
+ Process.
+
+ Receive XOFF:
+ If prop#340 is enabled, verify packed with SENDME
+ Enter STREAM_XOFF
+
+ Receive XON:
+ If prop#340 is enabled, verify packed with SENDME
+ Verify rate has changed
+
+ Send END:
+ Enter END_SENT.
+
+ Receive END:
+ Enter TCP_STREAM_START
+
+END_SENT:
+ Same as STREAM_OPEN, except do not actually deliver data.
+ Only remain in this state for one RTT_max, or until END_ACK.
+```
+
+
+### Conflux Handler
+
+Purpose: Circuits that are a part of a conflux set have a conflux handler, associated
+with the last hop.
+
+Trigger: Creation of a conflux set
+
+Message Scope: Circuit ID and hop number must match
+ - XXX: Linked circuits must accept stream ids from either circuit for other
+ handlers :/
+
+```text
+CONFLUX_START: (all conflux leg circuits start here)
+ Upon sending CONFLUX_LINK:
+ Enter CONFLUX_LINKING
+
+CONFLUX_LINKING:
+ Receiving CONFLUX_LINKED:
+ Send CONFLUX_LINKED_ACK
+ Enter CONFLUX_LINKED
+
+CONFLUX_LINKED:
+ Receiving CONFLUX_SWITCH:
+ If prop#340 is negotiated, ensure packed with a DATA cell
+```
+
+
+### UDP Stream Handler
+
+Purpose: Circuits that are using prop#339
+
+Trigger: UDP stream creation
+
+Message Scope: Circuit ID, hop number, and stream-id must match
+
+```text
+UDP_STREAM_START:
+ If no other udp streams used on circuit:
+ Send CONNECT_UDP for any stream, enter UDP_CONNECTING
+ else:
+ Immediately enter UDP_CONNECTING
+ (CONNECTED_UDP MAY arrive without a CONNECT_UDP, after the first UDP
+ stream on a circuit is established)
+
+UDP_CONNECTING:
+ Upon receipt of CONNECTED_UDP, enter UDP_CONNECTED
+
+UDP_CONNECTED:
+ Receive DATAGRAM:
+ Verify length > 0
+ Verify Prop#344 NAT rules are obeyed, including srcport and stream limits
+ Process.
+
+ Send END:
+ Enter UDP_END_SENT
+
+UDP_END_SENT:
+ Same as UDP_CONNECTED, except do not actually deliver data.
+ Only remain in this state for one RTT_max, or until END_ACK,
+ then transition to UDP_STREAM_START.
+```
+
+
+# HSDIR Inflation { #HSDIRINFLATION }
+
+XXX: This can be folded into the state machines and/or rend-spec.. The state
+machines should actually be able to handle this, once they are ready for it.
+
+One of the most common questions about dropped cells is "what about data cells
+with a 1 byte payload?". As Prop#344 makes clear, this is not a dropped cell
+attack, but is instead an instance of an Active Traffic Manipulation Covert
+Channel, described in Section 1.3.2. The lower severity of active traffic
+manipulation is due to the fact that it cannot be used to deanonymize 100% of
+a target client's circuits, where as the combination of path bias and
+pre-usage dropped cells can.
+
+However, there is one case where one can construct a potent attack from this
+Active Traffic Manipulation: by making use of onion service circuits being
+built on demand by an application. Further, because the onion service
+handshake is uniquely fingerprintable (see Section 1.2.1 of Prop#344), it is
+possible to use this vector in this specific case to encode an identifier in
+the timing and traffic patterns of the onion service descriptor download,
+similar to how the CMU attack operated, and use both the onion service
+fingerprint and descriptor traffic pattern to transmit the fact that a
+particular onion service was visited, to the Guard or possibly even a local
+network observer.
+
+A normal hidden service descriptor occupies only ~10 cells (with a hard max of
+30KB, or ~60 cells). This is not enough to reliably encode the full address of
+the onion service in a timing-based covert channel.
+
+However, there are two ways to cause this descriptor download to transmit
+enough data to encode such a covert channel, and replicate the CMU attack
+using timing information of this data.
+
+First, the actual descriptor payload can be spread across many DATA cells that
+are filled only partially with data (which does not happen if the HSDIR is
+honest and well-behaved, because it always has the full descriptor on hand).
+
+Second, in C-tor, additional junk can be appended at the end of a onion service
+descriptor document that does not count against the 30KB maximum, which the
+client will happily download and then ignore.
+
+Neither of these things are necessary to preserve, and neither can happen in
+normal operation. They can either be addressed directly by checks on
+HSDIR-based RELAY_COMMAND_DATA lengths and descriptor parsing, or by simply
+enforcing that circuits used to fetch service descriptors can *only* receive
+as many bytes as the maximum descriptor size, before being closed.
+
+XXX: Consider RELAY_COMMAND_END_ACK also..
+ - https://gitlab.torproject.org/tpo/core/torspec/-/issues/196
+
+XXX: Tickets to grovel through for other stuff:
+https://gitlab.torproject.org/tpo/core/torspec/-/issues/38
+https://gitlab.torproject.org/tpo/core/torspec/-/issues/39
+https://gitlab.torproject.org/tpo/core/arti/-/issues/525
+
+
+# Command Allowlist enumeration { #CTORALLOWLIST }
+
+XXX: We are planning to remove this section after we finish the state
+machines; keeping it for reference until then for cross-checking.
+
+Formerly, in C-Tor, we took the approach of performing a series of checks for
+each message command, ad-hoc. Here's those rules, for spot-checking that the
+above state machines cover them.
+
+All relay commands are rejected by clients and serviced unless a rule says
+they are OK.
+
+Here's a list of those rules, by relay command:
+
+ - RELAY_COMMAND_DATA 2
+ - This command MUST only arrive for valid open or half-open stream ID
+ - This command MUST have data length > 0
+ - On HSDIR circuits, ONLY ONE command is allowed to have a non-full
+ payload (the last command). See Section 4.
+
+ - RELAY_COMMAND_END 3
+ - This command MUST only arrive ONCE for each valid open or half-open
+ stream ID
+
+ - RELAY_COMMAND_CONNECTED 4
+ - This command MUST ONLY be accepted ONCE by clients if they sent a BEGIN
+ or BEGIN_DIR
+ - The stream ID MUST match the stream ID from BEGIN (or BEGIN_DIR)
+
+ - RELAY_COMMAND_DROP 10
+ - This command is accepted by clients from any hop that they
+ have negotiated an active circuit padding machine with
+
+ - RELAY_COMMAND_CONFLUX_LINKED 20
+ - Ensure that a LINK cell was sent to the hop that sent this
+ - Ensure that no previous LINKED cell has arrived on this circuit
+
+ - RELAY_COMMAND_CONFLUX_SWITCH 22
+ - Ensure that conflux is enabled and linked
+ - If Prop#340 is in use, this cell MUST be packed with a valid
+ multiplexed RELAY_COMMAND_DATA cell.
+
+ - RELAY_COMMAND_INTRODUCE2 35
+ - Services MUST check:
+ - The intro is for a valid service identity and auth
+ - The command has a valid sub-credential
+ - The command is not a replay (possibly not close circuit?)
+
+ - RELAY_COMMAND_RENDEZVOUS2 37
+ - This command MUST ONLY arrive ONCE in response to a sent REND1 cell,
+ on the appropriate circuit
+ - The ntor handshake must succeed with MAC validation
+
+ - RELAY_COMMAND_INTRO_ESTABLISHED 38
+ - Services MUST check:
+ - This cell MUST ONLY come ONCE in response to
+ RELAY_COMMAND_ESTABLISH_INTRO, for the appropriate service identity
+
+ - RELAY_COMMAND_RENDEZVOUS_ESTABLISHED 39
+ - This command MUST ONLY be accepted ONCE in response to
+ RELAY_COMMAND_ESTABLISH_RENDEZVOUS
+
+ - RELAY_COMMAND_INTRODUCE_ACK 40
+ - This command MUST ONLY be accepted ONCE by clients, in response to
+ RELAY_COMMAND_INTRODUCE1
+
+ - RELAY_COMMAND_PADDING_NEGOTIATED 42
+ - This command MUST ONLY be accepted by clients in response to
+ PADDING_NEGOTIATE
+
+ - RELAY_COMMAND_XOFF 43
+ - Ensure that congestion control is enabled and negotiated
+ - Ensure that the stream id is either opened or half-open
+ - Ensure that the stream id is in "XON" state
+
+ - RELAY_COMMAND_XON 44
+ - Ensure that congestion control is enabled and negotiated
+ - Ensure that the stream id is either opened or half-open
+ - Enforce always packing this to a SENDME with Prop#340?
+
+ - RELAY_COMMAND_CONNECTED_UDP
+ - The stream id in this command MUST match that from
+ RELAY_COMMAND_CONNECT_UDP
+ - This command is only accepted once per UDP stream id
+
+ - RELAY_COMMAND_DATAGRAM
+ - This command MUST only arrive for valid open or half-open stream ID
+ - This command MUST have data length > 0
+
+
+
+References:
+
+[DROPMARK]: https://petsymposium.org/2018/files/papers/issue2/popets-2018-0011.pdf
diff --git a/proposals/BY_INDEX.md b/proposals/BY_INDEX.md
index a8f1ac4..96e52b0 100644
--- a/proposals/BY_INDEX.md
+++ b/proposals/BY_INDEX.md
@@ -266,4 +266,5 @@ Below are a list of proposals sorted by their proposal number. See
* [`346-protovers-again.md`](/proposals/346-protovers-again.md): Clarifying and extending the use of protocol versioning \[OPEN\]
* [`347-domain-separation.md`](/proposals/347-domain-separation.md): Domain separation for certificate signing keys \[OPEN\]
* [`348-udp-app-support.md`](/proposals/348-udp-app-support.md): UDP Application Support in Tor \[OPEN\]
+* [`349-command-state-validation.md`](/proposals/349-command-state-validation.md): Client-Side Command Acceptance Validation \[DRAFT\]
diff --git a/proposals/BY_STATUS.md b/proposals/BY_STATUS.md
index 0c265c2..84345ac 100644
--- a/proposals/BY_STATUS.md
+++ b/proposals/BY_STATUS.md
@@ -103,6 +103,7 @@ discussion.
* [`316-flashflow.md`](/proposals/316-flashflow.md): FlashFlow: A Secure Speed Test for Tor (Parent Proposal)
* [`331-res-tokens-for-anti-dos.md`](/proposals/331-res-tokens-for-anti-dos.md): Res tokens: Anonymous Credentials for Onion Service DoS Resilience
* [`342-decouple-hs-interval.md`](/proposals/342-decouple-hs-interval.md): Decoupling hs_interval and SRV lifetime
+* [`349-command-state-validation.md`](/proposals/349-command-state-validation.md): Client-Side Command Acceptance Validation
## NEEDS-REVISION proposals: ideas that we can't implement as-is
diff --git a/proposals/SUMMARY.md b/proposals/SUMMARY.md
index df02273..09e1270 100644
--- a/proposals/SUMMARY.md
+++ b/proposals/SUMMARY.md
@@ -259,6 +259,7 @@
- [`346-protovers-again`](./346-protovers-again.md): Clarifying and extending the use of protocol versioning (OPEN)
- [`347-domain-separation`](./347-domain-separation.md): Domain separation for certificate signing keys (OPEN)
- [`348-udp-app-support`](./348-udp-app-support.md): UDP Application Support in Tor (OPEN)
+ - [`349-command-state-validation`](./349-command-state-validation.md): Client-Side Command Acceptance Validation (DRAFT)