diff options
Diffstat (limited to 'spec/tor-spec/relay-cells.md')
-rw-r--r-- | spec/tor-spec/relay-cells.md | 175 |
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. |