aboutsummaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorJim Newsome <jnewsome@torproject.org>2023-11-08 13:20:30 -0600
committerJim Newsome <jnewsome@torproject.org>2023-11-08 13:20:30 -0600
commit4436b5235596e90c9bf4ec6d2b6979f16e3ac11e (patch)
treed87a11910c99e05e1ecc42bd75c5669d39e6dc43 /spec
parentf223ae81c6d8a7370b7a0c50aeb6528eb134de3b (diff)
downloadtorspec-4436b5235596e90c9bf4ec6d2b6979f16e3ac11e.tar.gz
torspec-4436b5235596e90c9bf4ec6d2b6979f16e3ac11e.zip
create-created-cells.md: Reformat code blocks and quote identifiers
Diffstat (limited to 'spec')
-rw-r--r--spec/tor-spec/create-created-cells.md472
1 files changed, 244 insertions, 228 deletions
diff --git a/spec/tor-spec/create-created-cells.md b/spec/tor-spec/create-created-cells.md
index d649f7c..c09c96d 100644
--- a/spec/tor-spec/create-created-cells.md
+++ b/spec/tor-spec/create-created-cells.md
@@ -69,7 +69,7 @@ The format of a CREATED cell is:
(It's equivalent to a CREATED2 cell with length of `TAP_S_HANDSHAKE_LEN`.)
-As usual with DH, x and y MUST be generated randomly.
+As usual with DH, `x` and `y` MUST be generated randomly.
In general, clients SHOULD use CREATE whenever they are using the TAP
handshake, and CREATE2 otherwise. Clients SHOULD NOT send the
@@ -125,31 +125,26 @@ RELAY_EARLY cell to the last node in the circuit.
An EXTEND2 cell's relay payload contains:
-```text
- NSPEC (Number of link specifiers) [1 byte]
- NSPEC times:
- LSTYPE (Link specifier type) [1 byte]
- LSLEN (Link specifier length) [1 byte]
- LSPEC (Link specifier) [LSLEN bytes]
- HTYPE (Client Handshake Type) [2 bytes]
- HLEN (Client Handshake Data Len) [2 bytes]
- HDATA (Client Handshake Data) [HLEN bytes]
-```
+| Field | Description | Size |
+| ----- | ----------- | ---- |
+| `NSPEC` | Number of link specifiers | 1 byte
+| `NSPEC` times: |
+| - `LSTYPE` | Link specifier type | 1 byte
+| - `LSLEN` | Link specifier length | 1 byte
+| - `LSPEC` | Link specifier | LSLEN bytes
+| `HTYPE` | Client Handshake Type | 2 bytes
+| `HLEN` | Client Handshake Data Len | 2 bytes
+| `HDATA` | Client Handshake Data | HLEN bytes
Link specifiers describe the next node in the circuit and how to
connect to it. Recognized specifiers are:
-```text
- [00] TLS-over-TCP, IPv4 address
- A four-byte IPv4 address plus two-byte ORPort
- [01] TLS-over-TCP, IPv6 address
- A sixteen-byte IPv6 address plus two-byte ORPort
- [02] Legacy identity
- A 20-byte SHA1 identity fingerprint. At most one may be listed.
- [03] Ed25519 identity
- A 32-byte Ed25519 identity fingerprint. At most one may
- be listed.
-```
+| Value | Description
+| ----- | -----------
+| [00] | TLS-over-TCP, IPv4 address. A four-byte IPv4 address plus two-byte ORPort.
+| [01] | TLS-over-TCP, IPv6 address. A sixteen-byte IPv6 address plus two-byte ORPort.
+| [02] | Legacy identity. A 20-byte SHA1 identity fingerprint. At most one may be listed.
+| [03] | Ed25519 identity. A 32-byte Ed25519 identity fingerprint. At most one may be listed.
Nodes MUST ignore unrecognized specifiers, and MUST accept multiple
instances of specifiers other than 'legacy identity' and
@@ -162,12 +157,12 @@ these link specifiers, if using them, in this order: \[00\], \[02\], \[03\],
The relay payload for an EXTEND relay cell consists of:
-```text
- Address [4 bytes]
- Port [2 bytes]
- Onion skin [TAP_C_HANDSHAKE_LEN bytes]
- Identity fingerprint [HASH_LEN bytes]
-```
+| Field | Size
+| ----- | ----
+| Address | 4 bytes
+| Port | 2 bytes
+| Onion skin | `TAP_C_HANDSHAKE_LEN` bytes
+| Identity fingerprint | `HASH_LEN` bytes
The "legacy identity" and "identity fingerprint" fields are the
SHA1 hash of the PKCS#1 ASN1 encoding of the next onion router's
@@ -223,55 +218,54 @@ use the format with 'client handshake type tag'.
## The "TAP" handshake {#TAP}
-This handshake uses Diffie-Hellman in Z_p and RSA to compute a set of
+This handshake uses Diffie-Hellman in Z<sub>p</sub> and RSA to compute a set of
shared keys which the client knows are shared only with a particular
server, and the server knows are shared with whomever sent the
original handshake (or with nobody at all). It's not very fast and
not very good. (See Goldberg's "On the Security of the Tor
Authentication Protocol".)
-Define TAP_C_HANDSHAKE_LEN as DH_LEN+KEY_LEN+KP_PAD_LEN.
-Define TAP_S_HANDSHAKE_LEN as DH_LEN+HASH_LEN.
+Define `TAP_C_HANDSHAKE_LEN` as `DH_LEN+KEY_LEN+KP_PAD_LEN`.
+Define `TAP_S_HANDSHAKE_LEN` as `DH_LEN+HASH_LEN`.
The payload for a CREATE cell is an 'onion skin', which consists of
-the first step of the DH handshake data (also known as g^x). This
+the first step of the DH handshake data (also known as `g^x`). This
value is encrypted using the "legacy hybrid encryption" algorithm
(see ["Preliminaries ยป A bad hybrid encryption algorithm..."](./preliminaries.md#legacy-hybrid-encryption))
to the server's onion key, giving a client handshake:
-```text
- KP-encrypted:
- Padding [KP_PAD_LEN bytes]
- Symmetric key [KEY_LEN bytes]
- First part of g^x [KP_ENC_LEN-KP_PAD_LEN-KEY_LEN bytes]
- Symmetrically encrypted:
- Second part of g^x [DH_LEN-(KP_ENC_LEN-KP_PAD_LEN-KEY_LEN)
- bytes]
-```
+| Field | Size
+| ----- | ----
+| KP-encrypted:
+| - Padding | `KP_PAD_LEN` bytes
+| - Symmetric key | `KEY_LEN` bytes
+| - First part of `g^x` | `KP_ENC_LEN-KP_PAD_LEN-KEY_LEN` bytes
+| Symmetrically encrypted
+| - Second part of `g^x` | `DH_LEN-(KP_ENC_LEN-KP_PAD_LEN-KEY_LEN)` bytes
The payload for a CREATED cell, or the relay payload for an
EXTENDED cell, contains:
-```text
- DH data (g^y) [DH_LEN bytes]
- Derivative key data (KH) [HASH_LEN bytes] <see 5.2 below>
-```
+| Field | Size
+| ----- | ----
+| DH data (`g^y`) | `DH_LEN` bytes
+| Derivative key data (`KH`) | `HASH_LEN` bytes (see 5.2 below)
Once the handshake between the OP and an OR is completed, both can
-now calculate g^xy with ordinary DH. Before computing g^xy, both parties
-MUST verify that the received g^x or g^y value is not degenerate;
-that is, it must be strictly greater than 1 and strictly less than p-1
-where p is the DH modulus. Implementations MUST NOT complete a handshake
+now calculate `g^xy` with ordinary DH. Before computing `g^xy`, both parties
+MUST verify that the received `g^x` or `g^y` value is not degenerate;
+that is, it must be strictly greater than `1` and strictly less than `p-1`
+where `p` is the DH modulus. Implementations MUST NOT complete a handshake
with degenerate keys. Implementations MUST NOT discard other "weak"
-g^x values.
+`g^x` values.
(Discarding degenerate keys is critical for security; if bad keys
are not discarded, an attacker can substitute the OR's CREATED
-cell's g^y with 0 or 1, thus creating a known g^xy and impersonating
+cell's `g^y` with `0` or `1`, thus creating a known `g^xy` and impersonating
the OR. Discarding other keys may allow attacks to learn bits of
the private key.)
-Once both parties have g^xy, they derive their shared circuit keys
+Once both parties have `g^xy`, they derive their shared circuit keys
and 'derivative key data' value via the
[KDF-TOR function](./setting-circuit-keys.md#kdf-tor).
@@ -291,68 +285,74 @@ new Diffie-Hellman speed records" by D. J. Bernstein.
In this section, define:
```text
- H(x,t) as HMAC_SHA256 with message x and key t.
- H_LENGTH = 32.
- ID_LENGTH = 20.
- G_LENGTH = 32
- PROTOID = "ntor-curve25519-sha256-1"
- t_mac = PROTOID | ":mac"
- t_key = PROTOID | ":key_extract"
- t_verify = PROTOID | ":verify"
- G = The preferred base point for curve25519 ([9])
- KEYGEN() = The curve25519 key generation algorithm, returning
- a private/public keypair.
- m_expand = PROTOID | ":key_expand"
- KEYID(A) = A
- EXP(a, b) = The ECDH algorithm for establishing a shared secret.
+H(x,t) as HMAC_SHA256 with message x and key t.
+H_LENGTH = 32.
+ID_LENGTH = 20.
+G_LENGTH = 32
+PROTOID = "ntor-curve25519-sha256-1"
+t_mac = PROTOID | ":mac"
+t_key = PROTOID | ":key_extract"
+t_verify = PROTOID | ":verify"
+G = The preferred base point for curve25519 ([9])
+KEYGEN() = The curve25519 key generation algorithm, returning
+ a private/public keypair.
+m_expand = PROTOID | ":key_expand"
+KEYID(A) = A
+EXP(a, b) = The ECDH algorithm for establishing a shared secret.
```
To perform the handshake, the client needs to know an identity key
digest for the server, and an ntor onion key (a curve25519 public
-key) for that server. Call the ntor onion key "B". The client
+key) for that server. Call the ntor onion key `B`. The client
generates a temporary keypair:
+```text
x,X = KEYGEN()
+```
and generates a client-side handshake with contents:
-```text
- NODEID Server identity digest [ID_LENGTH bytes]
- KEYID KEYID(B) [H_LENGTH bytes]
- CLIENT_KP X [G_LENGTH bytes]
-```
+| Field | Value | Size
+| ----- | ----- | ----
+| `NODEID` | Server identity digest | `ID_LENGTH` bytes
+| `KEYID` | KEYID(B) | `H_LENGTH` bytes
+| `CLIENT_KP` | X | `G_LENGTH` bytes
-The server generates a keypair of y,Y = KEYGEN(), and uses its ntor
-private key 'b' to compute:
+The server generates a keypair of `y,Y = KEYGEN()`, and uses its ntor
+private key `b` to compute:
```text
- secret_input = EXP(X,y) | EXP(X,b) | ID | B | X | Y | PROTOID
- KEY_SEED = H(secret_input, t_key)
- verify = H(secret_input, t_verify)
- auth_input = verify | ID | B | Y | X | PROTOID | "Server"
-
- The server's handshake reply is:
+secret_input = EXP(X,y) | EXP(X,b) | ID | B | X | Y | PROTOID
+KEY_SEED = H(secret_input, t_key)
+verify = H(secret_input, t_verify)
+auth_input = verify | ID | B | Y | X | PROTOID | "Server"
+```
- SERVER_KP Y [G_LENGTH bytes]
- AUTH H(auth_input, t_mac) [H_LENGTH bytes]
+The server's handshake reply is:
- The client then checks Y is in G^* [see NOTE below], and computes
+| Field | Value | Size
+| ----- | ----- | ----
+| `SERVER_KP` | `Y` | `G_LENGTH` bytes
+| `AUTH` | `H(auth_input, t_mac)` | `H_LENGTH` bytes
- secret_input = EXP(Y,x) | EXP(B,x) | ID | B | X | Y | PROTOID
- KEY_SEED = H(secret_input, t_key)
- verify = H(secret_input, t_verify)
- auth_input = verify | ID | B | Y | X | PROTOID | "Server"
+The client then checks `Y` is in `G^*` [see NOTE below], and computes
- The client verifies that AUTH == H(auth_input, t_mac).
+```text
+secret_input = EXP(Y,x) | EXP(B,x) | ID | B | X | Y | PROTOID
+KEY_SEED = H(secret_input, t_key)
+verify = H(secret_input, t_verify)
+auth_input = verify | ID | B | Y | X | PROTOID | "Server"
```
-Both parties check that none of the EXP() operations produced the
+The client verifies that `AUTH == H(auth_input, t_mac)`.
+
+Both parties check that none of the `EXP()` operations produced the
point at infinity. \[NOTE: This is an adequate replacement for
-checking Y for group membership, if the group is curve25519.\]
+checking `Y` for group membership, if the group is curve25519.\]
-Both parties now have a shared value for KEY_SEED. They expand this
+Both parties now have a shared value for `KEY_SEED`. They expand this
into the keys needed for the Tor relay protocol, using the KDF
-described in 5.2.2 and the tag m_expand.
+described in 5.2.2 and the tag `m_expand`.
<a id="tor-spec.txt-5.1.4.1"></a>
@@ -371,137 +371,151 @@ To advertise support for this handshake, servers advertise the
In this handshake, we define:
```text
- PROTOID = "ntor3-curve25519-sha3_256-1"
- t_msgkdf = PROTOID | ":kdf_phase1"
- t_msgmac = PROTOID | ":msg_mac"
- t_key_seed = PROTOID | ":key_seed"
- t_verify = PROTOID | ":verify"
- t_final = PROTOID | ":kdf_final"
- t_auth = PROTOID | ":auth_final"
+PROTOID = "ntor3-curve25519-sha3_256-1"
+t_msgkdf = PROTOID | ":kdf_phase1"
+t_msgmac = PROTOID | ":msg_mac"
+t_key_seed = PROTOID | ":key_seed"
+t_verify = PROTOID | ":verify"
+t_final = PROTOID | ":kdf_final"
+t_auth = PROTOID | ":auth_final"
- `ENCAP(s)` -- an encapsulation function. We define this
- as `htonll(len(s)) | s`. (Note that `len(ENCAP(s)) = len(s) + 8`).
+`ENCAP(s)` -- an encapsulation function. We define this
+as `htonll(len(s)) | s`. (Note that `len(ENCAP(s)) = len(s) + 8`).
- `PARTITION(s, n1, n2, n3, ...)` -- a function that partitions a
- bytestring `s` into chunks of length `n1`, `n2`, `n3`, and so
- on. Extra data is put into a final chunk. If `s` is not long
- enough, the function fails.
+`PARTITION(s, n1, n2, n3, ...)` -- a function that partitions a
+bytestring `s` into chunks of length `n1`, `n2`, `n3`, and so
+on. Extra data is put into a final chunk. If `s` is not long
+enough, the function fails.
- H(s, t) = SHA3_256(ENCAP(t) | s)
- MAC(k, msg, t) = SHA3_256(ENCAP(t) | ENCAP(k) | s)
- KDF(s, t) = SHAKE_256(ENCAP(t) | s)
- ENC(k, m) = AES_256_CTR(k, m)
+H(s, t) = SHA3_256(ENCAP(t) | s)
+MAC(k, msg, t) = SHA3_256(ENCAP(t) | ENCAP(k) | s)
+KDF(s, t) = SHAKE_256(ENCAP(t) | s)
+ENC(k, m) = AES_256_CTR(k, m)
- EXP(pk,sk), KEYGEN: defined as in curve25519
+EXP(pk,sk), KEYGEN: defined as in curve25519
- DIGEST_LEN = MAC_LEN = MAC_KEY_LEN = ENC_KEY_LEN = PUB_KEY_LEN = 32
+DIGEST_LEN = MAC_LEN = MAC_KEY_LEN = ENC_KEY_LEN = PUB_KEY_LEN = 32
- ID_LEN = 32 (representing an ed25519 identity key)
+ID_LEN = 32 (representing an ed25519 identity key)
- For any tag "t_foo":
- H_foo(s) = H(s, t_foo)
- MAC_foo(k, msg) = MAC(k, msg, t_foo)
- KDF_foo(s) = KDF(s, t_foo)
+For any tag "t_foo":
+ H_foo(s) = H(s, t_foo)
+ MAC_foo(k, msg) = MAC(k, msg, t_foo)
+ KDF_foo(s) = KDF(s, t_foo)
+```
- Other notation is as in the ntor description in 5.1.4 above.
+Other notation is as in the ntor description in 5.1.4 above.
- The client begins by knowing:
+The client begins by knowing:
- B, ID -- The curve25519 onion key and Ed25519 ID of the server that it
- wants to use.
- CM -- A message it wants to send as part of its handshake.
- VER -- An optional shared verification string:
+```text
+B, ID -- The curve25519 onion key and Ed25519 ID of the server that it
+ wants to use.
+CM -- A message it wants to send as part of its handshake.
+VER -- An optional shared verification string:
+```
- The client computes:
+The client computes:
- x,X = KEYGEN()
- Bx = EXP(B,x)
- secret_input_phase1 = Bx | ID | X | B | PROTOID | ENCAP(VER)
- phase1_keys = KDF_msgkdf(secret_input_phase1)
- (ENC_K1, MAC_K1) = PARTITION(phase1_keys, ENC_KEY_LEN, MAC_KEY_LEN)
- encrypted_msg = ENC(ENC_K1, CM)
- msg_mac = MAC_msgmac(MAC_K1, ID | B | X | encrypted_msg)
+```text
+x,X = KEYGEN()
+Bx = EXP(B,x)
+secret_input_phase1 = Bx | ID | X | B | PROTOID | ENCAP(VER)
+phase1_keys = KDF_msgkdf(secret_input_phase1)
+(ENC_K1, MAC_K1) = PARTITION(phase1_keys, ENC_KEY_LEN, MAC_KEY_LEN)
+encrypted_msg = ENC(ENC_K1, CM)
+msg_mac = MAC_msgmac(MAC_K1, ID | B | X | encrypted_msg)
+```
- The client then sends, as its CREATE handshake:
+ The client then sends, as its CREATE handshake:
- NODEID ID [ID_LEN bytes]
- KEYID B [PUB_KEY_LEN bytes]
- CLIENT_PK X [PUB_KEY_LEN bytes]
- MSG encrypted_msg [len(CM) bytes]
- MAC msg_mac [MAC_LEN bytes]
+| Field | Value | Size
+| ----- | ----- | ----
+| `NODEID` | `ID` | `ID_LEN` bytes
+| `KEYID` | `B` | `PUB_KEY_LEN` bytes
+| `CLIENT_PK` | `X` | `PUB_KEY_LEN` bytes
+| `MSG` | `encrypted_msg` | `len(CM)` bytes
+| `MAC` | `msg_mac` | `MAC_LEN` bytes
- The client remembers x, X, B, ID, Bx, and msg_mac.
-```
+The client remembers `x`, `X`, `B`, `ID`, `Bx`, and `msg_mac`.
-When the server receives this handshake, it checks whether NODEID is as
-expected, and looks up the (b,B) keypair corresponding to KEYID. If the
-keypair is missing or the NODEID is wrong, the handshake fails.
+When the server receives this handshake, it checks whether `NODEID` is as
+expected, and looks up the `(b,B)` keypair corresponding to `KEYID`. If the
+keypair is missing or the `NODEID` is wrong, the handshake fails.
Now the relay uses `X=CLIENT_PK` to compute:
```text
- Xb = EXP(X,b)
- secret_input_phase1 = Xb | ID | X | B | PROTOID | ENCAP(VER)
- phase1_keys = KDF_msgkdf(secret_input_phase1)
- (ENC_K1, MAC_K1) = PARTITION(phase1_keys, ENC_KEY_LEN, MAC_KEY_LEN)
+Xb = EXP(X,b)
+secret_input_phase1 = Xb | ID | X | B | PROTOID | ENCAP(VER)
+phase1_keys = KDF_msgkdf(secret_input_phase1)
+(ENC_K1, MAC_K1) = PARTITION(phase1_keys, ENC_KEY_LEN, MAC_KEY_LEN)
- expected_mac = MAC_msgmac(MAC_K1, ID | B | X | MSG)
+expected_mac = MAC_msgmac(MAC_K1, ID | B | X | MSG)
```
If `expected_mac` is not `MAC`, the handshake fails. Otherwise
the relay computes `CM` as:
+```text
CM = DEC(MSG, ENC_K1)
+```
The relay then checks whether `CM` is well-formed, and in response
composes `SM`, the reply that it wants to send as part of the
handshake. It then generates a new ephemeral keypair:
+```text
y,Y = KEYGEN()
+```
and computes the rest of the handshake:
```text
- Xy = EXP(X,y)
- secret_input = Xy | Xb | ID | B | X | Y | PROTOID | ENCAP(VER)
- ntor_key_seed = H_key_seed(secret_input)
- verify = H_verify(secret_input)
+Xy = EXP(X,y)
+secret_input = Xy | Xb | ID | B | X | Y | PROTOID | ENCAP(VER)
+ntor_key_seed = H_key_seed(secret_input)
+verify = H_verify(secret_input)
- RAW_KEYSTREAM = KDF_final(ntor_key_seed)
- (ENC_KEY, KEYSTREAM) = PARTITION(RAW_KEYSTREAM, ENC_KEY_LKEN, ...)
+RAW_KEYSTREAM = KDF_final(ntor_key_seed)
+(ENC_KEY, KEYSTREAM) = PARTITION(RAW_KEYSTREAM, ENC_KEY_LKEN, ...)
- encrypted_msg = ENC(ENC_KEY, SM)
+encrypted_msg = ENC(ENC_KEY, SM)
- auth_input = verify | ID | B | Y | X | MAC | ENCAP(encrypted_msg) |
- PROTOID | "Server"
- AUTH = H_auth(auth_input)
-
- The relay then sends as its CREATED handshake:
+auth_input = verify | ID | B | Y | X | MAC | ENCAP(encrypted_msg) |
+ PROTOID | "Server"
+AUTH = H_auth(auth_input)
+```
- Y Y [PUB_KEY_LEN bytes]
- AUTH AUTH [DIGEST_LEN bytes]
- MSG encrypted_msg [len(SM) bytes, up to end of the message]
+The relay then sends as its CREATED handshake:
- Upon receiving this handshake, the client computes:
+| Field | Value | Size
+| ----- | ----- | ----
+| `Y` | `Y` | `PUB_KEY_LEN` bytes
+| `AUTH` | `AUTH` | `DIGEST_LEN` bytes
+| `MSG` | `encrypted_msg` | `len(SM)` bytes, up to end of the message
- Yx = EXP(Y, x)
- secret_input = Yx | Bx | ID | B | X | Y | PROTOID | ENCAP(VER)
- ntor_key_seed = H_key_seed(secret_input)
- verify = H_verify(secret_input)
+Upon receiving this handshake, the client computes:
- auth_input = verify | ID | B | Y | X | MAC | ENCAP(MSG) |
- PROTOID | "Server"
- AUTH_expected = H_auth(auth_input)
+```text
+Yx = EXP(Y, x)
+secret_input = Yx | Bx | ID | B | X | Y | PROTOID | ENCAP(VER)
+ntor_key_seed = H_key_seed(secret_input)
+verify = H_verify(secret_input)
+
+auth_input = verify | ID | B | Y | X | MAC | ENCAP(MSG) |
+ PROTOID | "Server"
+AUTH_expected = H_auth(auth_input)
```
-If AUTH_expected is equal to AUTH, then the handshake has
+If `AUTH_expected` is equal to `AUTH`, then the handshake has
succeeded. The client can then calculate:
```text
- RAW_KEYSTREAM = KDF_final(ntor_key_seed)
- (ENC_KEY, KEYSTREAM) = PARTITION(RAW_KEYSTREAM, ENC_KEY_LKEN, ...)
+RAW_KEYSTREAM = KDF_final(ntor_key_seed)
+(ENC_KEY, KEYSTREAM) = PARTITION(RAW_KEYSTREAM, ENC_KEY_LKEN, ...)
- SM = DEC(ENC_KEY, MSG)
+SM = DEC(ENC_KEY, MSG)
```
SM is the message from the relay, and the client uses KEYSTREAM to
@@ -524,18 +538,20 @@ created.
A CREATE_FAST cell contains:
-Key material (X) \[HASH_LEN bytes\]
+| Field | Size
+| ----- | ----
+| Key material (`X`) | `HASH_LEN` bytes
A CREATED_FAST cell contains:
-```text
- Key material (Y) [HASH_LEN bytes]
- Derivative key data [HASH_LEN bytes] (See 5.2.1 below)
+| Field | Size
+| ----- | ----
+| Key material (`Y`) | `HASH_LEN` bytes
+| Derivative key data| `HASH_LEN` bytes (See 5.2.1 below)
- The values of X and Y must be generated randomly.
-```
+The values of `X` and `Y` must be generated randomly.
-Once both parties have X and Y, they derive their shared circuit keys
+Once both parties have `X` and `Y`, they derive their shared circuit keys
and 'derivative key data' value via the KDF-TOR function in 5.2.1.
The CREATE_FAST handshake is currently deprecated whenever it is not
@@ -553,51 +569,51 @@ relay to send additional data as part of the handshake. When used in a
CREATE/CREATED handshake, this additional data must have the following
format:
-```text
- N_EXTENSIONS [one byte]
- N_EXTENSIONS times:
- EXT_FIELD_TYPE [one byte]
- EXT_FIELD_LEN [one byte]
- EXT_FIELD [EXT_FIELD_LEN bytes]
-
- (`EXT_FIELD_LEN` may be zero, in which case EXT_FIELD is absent.)
-
- All parties MUST reject messages that are not well-formed per the
- rules above.
-
- We do not specify specific TYPE semantics here; we leave those for
- other proposals and specifications.
-
- Parties MUST ignore extensions with `EXT_FIELD_TYPE` bodies they do not
- recognize.
-
- Unless otherwise specified in the documentation for an extension type:
- * Each extension type SHOULD be sent only once in a message.
- * Parties MUST ignore any occurrences all occurrences of an extension
- with a given type after the first such occurrence.
- * Extensions SHOULD be sent in numerically ascending order by type.
-
- (The above extension sorting and multiplicity rules are only defaults;
- they may be overridden in the description of individual extensions.)
-
- Currently supported extensions are:
-
- 1 -- CC_FIELD_REQUEST [Client to server]
-
- Contains an empty payload. Signifies that the client
- wants to use the extended congestion control described
- in proposal 324.
-
- 2 -- CC_FIELD_RESPONSE [Server to client]
-
- Indicates that the relay will use the congestion control
- of proposal 324, as requested by the client. One byte
- in length:
-
- sendme_inc [1 byte]
-
- 3 -- Subprotocol Request [Client to Server]
-
- (RESERVED) Tells the endpoint what protocol version to use on the
- circuit (prop346).
-```
+| Field | Size
+| ----- | ----
+| `N_EXTENSIONS` | one byte
+| `N_EXTENSIONS` times: |
+| - `EXT_FIELD_TYPE` | one byte
+| - `EXT_FIELD_LEN` | one byte
+| - `EXT_FIELD` | `EXT_FIELD_LEN` bytes
+
+(`EXT_FIELD_LEN` may be zero, in which case `EXT_FIELD` is absent.)
+
+All parties MUST reject messages that are not well-formed per the
+rules above.
+
+We do not specify specific TYPE semantics here; we leave those for
+other proposals and specifications.
+
+Parties MUST ignore extensions with `EXT_FIELD_TYPE` bodies they do not
+recognize.
+
+Unless otherwise specified in the documentation for an extension type:
+ * Each extension type SHOULD be sent only once in a message.
+ * Parties MUST ignore any occurrences all occurrences of an extension
+ with a given type after the first such occurrence.
+ * Extensions SHOULD be sent in numerically ascending order by type.
+
+(The above extension sorting and multiplicity rules are only defaults;
+they may be overridden in the description of individual extensions.)
+
+Currently supported extensions are:
+
+ * 1 -- `CC_FIELD_REQUEST` [Client to server]
+
+ Contains an empty payload. Signifies that the client
+ wants to use the extended congestion control described
+ in proposal 324.
+
+ * 2 -- `CC_FIELD_RESPONSE` [Server to client]
+
+ Indicates that the relay will use the congestion control
+ of proposal 324, as requested by the client. One byte
+ in length:
+
+ `sendme_inc [1 byte]`
+
+ * 3 -- Subprotocol Request [Client to Server]
+
+ (RESERVED) Tells the endpoint what protocol version to use on the
+ circuit (prop346).