aboutsummaryrefslogtreecommitdiff
path: root/spec/tor-spec/relay-cells.md
diff options
context:
space:
mode:
Diffstat (limited to 'spec/tor-spec/relay-cells.md')
-rw-r--r--spec/tor-spec/relay-cells.md175
1 files changed, 175 insertions, 0 deletions
diff --git a/spec/tor-spec/relay-cells.md b/spec/tor-spec/relay-cells.md
new file mode 100644
index 0000000..346899b
--- /dev/null
+++ b/spec/tor-spec/relay-cells.md
@@ -0,0 +1,175 @@
+<a id="tor-spec.txt-6.1"></a>
+
+# Relay cells {#relay-cells}
+
+Within a circuit, the OP and the end node use the contents of
+relay cells to tunnel end-to-end commands and TCP connections
+("Streams") across circuits. End-to-end commands can be initiated
+by either edge; streams are initiated by the OP.
+
+End nodes that accept streams may be:
+
+- exit relays (RELAY_BEGIN, anonymous),
+- directory servers (RELAY_BEGIN_DIR, anonymous or non-anonymous),
+- onion services (RELAY_BEGIN, anonymous via a rendezvous point).
+
+The payload of each unencrypted relay cell consists of an
+enveloped relay message, encoded as follows:
+
+| Field | Size
+| ----- | ----
+| Relay command | 1 byte
+| 'Recognized' | 2 bytes
+| StreamID | 2 bytes
+| Digest | 4 bytes
+| Length | 2 bytes
+| Data | Length bytes
+| Padding | PAYLOAD_LEN - 11 - Length bytes
+
+> TODO: When we implement [prop340](../proposals/340-packed-and-fragmented.md),
+> we should clarify which parts of the above are about
+> the relay cell, and which are the enveloped message.
+
+The relay commands are:
+
+| Command | Identifier | Direction | Control?
+| ------- | ---------- | --------- | --------
+| 1 | RELAY_BEGIN | forward |
+| 2 | RELAY_DATA | forward or backward |
+| 3 | RELAY_END | forward or backward |
+| 4 | RELAY_CONNECTED | backward |
+| 5 | RELAY_SENDME | forward or backward | sometimes control
+| 6 | RELAY_EXTEND | forward | control
+| 7 | RELAY_EXTENDED | backward | control
+| 8 | RELAY_TRUNCATE | forward | control
+| 9 | RELAY_TRUNCATED | backward | control
+| 10 | RELAY_DROP | forward or backward | control
+| 11 | RELAY_RESOLVE | forward |
+| 12 | RELAY_RESOLVED | backward |
+| 13 | RELAY_BEGIN_DIR | forward |
+| 14 | RELAY_EXTEND2 | forward | control
+| 15 | RELAY_EXTENDED2 | backward | control
+| 16..18 | Reserved for UDP; Not yet in use, see [prop339].
+| 19..22 | Reserved for Conflux, see [prop329].
+| 32..40 | Used for hidden services; see the [rendezvous spec].
+| 41..42 | Used for circuit padding; see ["Circuit-level padding"] in the padding spec.
+| 43 | XON (See Sec 4 of [prop324]) | forward or backward |
+| 44 | XOFF (See Sec 4 of [prop324]) | forward or backward |
+
+[prop324]: ../proposals/324-rtt-congestion-control.txt
+[prop329]: ../proposals/329-traffic-splitting.md
+[prop339]: ../proposals/339-udp-over-tor.md
+[rendezvous spec]: ../rend-spec/index.md
+["Circuit-level padding"]: ../padding-spec/circuit-level-padding.md#circuit-level-padding
+
+Commands labelled as "forward" must only be sent by the originator
+of the circuit. Commands labelled as "backward" must only be sent by
+other nodes in the circuit back to the originator. Commands marked
+as either can be sent either by the originator or other nodes.
+
+The 'recognized' field is used as a simple indication that the cell
+is still encrypted. It is an optimization to avoid calculating
+expensive digests for every cell. When sending cells, the unencrypted
+'recognized' MUST be set to zero.
+
+When receiving and decrypting cells the 'recognized' will always be
+zero if we're the endpoint that the cell is destined for. For cells
+that we should relay, the 'recognized' field will usually be nonzero,
+but will accidentally be zero with P=2^-16.
+
+When handling a relay cell, if the 'recognized' in field in a
+decrypted relay payload is zero, the 'digest' field is computed as
+the first four bytes of the running digest of all the bytes that have
+been destined for this hop of the circuit or originated from this hop
+of the circuit, seeded from Df or Db respectively (obtained in
+[Setting circuit keys](./setting-circuit-keys.md#setting-circuit-keys)),
+and including this relay cell's entire payload
+(taken with the digest field set to zero). Note that these digests
+_do_ include the padding bytes at the end of the cell, not only those up
+to "Len". If the digest is correct, the cell is considered "recognized"
+for the purposes of decryption (see
+[Routing relay cells](./routing-relay-cells.md#routing-relay-cells)).
+
+(The digest does not include any bytes from relay cells that do
+not start or end at this hop of the circuit. That is, it does not
+include forwarded data. Therefore if 'recognized' is zero but the
+digest does not match, the running digest at that node should
+not be updated, and the cell should be forwarded on.)
+
+All relay messages pertaining to the same tunneled stream have the same
+stream ID. StreamIDs are chosen arbitrarily by the OP. No stream
+may have a StreamID of zero. Rather, relay messages that affect the
+entire circuit rather than a particular stream use a StreamID of zero
+-- they are marked in the table above as "\[control\]" style
+cells. (Sendme cells are marked as "sometimes control" because they
+can include a StreamID or not depending on their purpose -- see
+[Flow control](./flow-control.md#flow-control).)
+
+The 'Length' field of a relay cell contains the number of bytes in
+the relay payload which contain real payload data. The remainder of
+the unencrypted payload is padded with padding bytes. Implementations
+handle padding bytes of unencrypted relay cells as they do padding
+bytes for other cell types; see [Cell Packet format](./cell-packet-format.md#cell-packet-format).
+
+<span id="relay-cell-padding">The
+'Padding' field is used to make relay cell contents unpredictable, to
+avoid certain attacks (see
+[proposal 289](../proposals/289-authenticated-sendmes.txt)
+for rationale). Implementations
+SHOULD fill this field with four zero-valued bytes, followed by as many
+random bytes as will fit. (If there are fewer than 4 bytes for padding,
+then they should all be filled with zero.</span>
+
+Implementations MUST NOT rely on the contents of the 'Padding' field.
+
+If the relay cell is recognized but the relay command is not
+understood, the cell must be dropped and ignored. Its contents
+still count with respect to the digests and flow control windows, though.
+
+<a id="tor-spec.txt-6.1.1"></a>
+
+## Calculating the 'Digest' field {#digest-field}
+
+The 'Digest' field itself serves the purpose to check if a cell has been
+fully decrypted, that is, all onion layers have been removed. Having a
+single field, namely 'Recognized' is not sufficient, as outlined above.
+
+When ENCRYPTING a relay cell, an implementation does the following:
+
+```text
+# Encode the cell in binary (recognized and digest set to zero)
+tmp = cmd + [0, 0] + stream_id + [0, 0, 0, 0] + length + data + padding
+
+# Update the digest with the encoded data
+digest_state = hash_update(digest_state, tmp)
+digest = hash_calculate(digest_state)
+
+# The encoded data is the same as above with the digest field not being
+# zero anymore
+encoded = cmd + [0, 0] + stream_id + digest[0..4] + length + data +
+ padding
+
+# Now we can encrypt the cell by adding the onion layers ...
+```
+
+ When DECRYPTING a relay cell, an implementation does the following:
+
+```text
+decrypted = decrypt(cell)
+
+# Replace the digest field in decrypted by zeros
+tmp = decrypted[0..5] + [0, 0, 0, 0] + decrypted[9..]
+
+# Update the digest field with the decrypted data and its digest field
+# set to zero
+digest_state = hash_update(digest_state, tmp)
+digest = hash_calculate(digest_state)
+
+if digest[0..4] == decrypted[5..9]
+ # The cell has been fully decrypted ...
+```
+
+The caveat itself is that only the binary data with the digest bytes set to
+zero are being taken into account when calculating the running digest. The
+final plain-text cells (with the digest field set to its actual value) are
+not taken into the running digest.