Filename: 364-create-onehop.md
Title: CreateOnehop handshake to replace CREATE_FAST
Author: Nick Mathewson
Created: 29 April 2025
Status: Open
Introduction
The Tor protocol has a CREATE_FAST handshake that contains no public-key cryptography. It is used to make one-hop directory circuits, where privacy isn't much needed, and the TLS handshake is sufficient to guarantee encryption and authentication.
Clients can't avoid using CREATE_FAST: unlike other handshakes (ntor, ntor-v3), CREATE_FAST does not require the client to know an onion key for the relay. Therefore, CREATE_FAST is necessary when the client doesn't have a directory.
CREATE_FAST needs to be modernized, for two reasons.
Less importantly, CREATE_FAST ties us to some obsolete protocol elements, such as KDF-TOR and in-protocol KH. Even though TAP is gone, CREATE_FAST preserves its legacy, and forces implementations to carry obsolete code.
More importantly, CREATE_FAST has no support for additional data, and as such has no way to send extensions. This means that one-hop directory circuits have no way to enable modern features like congestion control or counter galois onion encryption.
Now, it's not super important for one-hop directory circuits to actually use those features, but not being able to turn those features on means that we can't fully deprecate stream-level SENDMEs and old-fashioned relay encryption as we would prefer to do.
The CreateOnehop handshake.
We introduce a new format for use in CREATE2 cells,
called CreateOnehop
.
The initiator's request is:
Field | Size |
---|---|
X | 32 bytes |
N_EXTENSIONS | one byte |
N_EXTENSIONS times: | |
- EXT_FIELD_TYPE | one byte |
- EXT_FIELD_LEN | one byte |
- EXT_FIELD | EXT_FIELD_LEN bytes |
The response is:
Field | Size |
---|---|
Y | 32 bytes |
N_EXTENSIONS | one byte |
N_EXTENSIONS times: | |
- EXT_FIELD_TYPE | one byte |
- EXT_FIELD_LEN | one byte |
- EXT_FIELD | EXT_FIELD_LEN bytes |
Clients MUST NOT send CreateOnehop in an EXTEND2 message; it is only valid in a CREATE2 cell. Relays MUST NOT accept a CreateOnehop in an EXTEND2 message.
If a circuit was created with CreateOneHop, relays SHOULD reject any attempt to extend that circuit.
The N_EXTENSIONS
, EXT_FIELD_TYPE
, EXT_FIELD_LEN
, and EXT_FIELD
fields in this handshake are equivalent to those in ntor-v3
and ht-ntor handshakes.
Upon completing this handshake, the parties use
KDF(X | Y, "CreateOneHop")
to derive their key material,
where KDF is the tagged SHAKE-256 defined in ntor-v3:
KDF(s, t) = SHAKE_256(ENCAP(t) | s)
where ENCAP(s) = htonll(len(s)) | s
Advertising support for CreateOnehop.
The presence of the CreateOneHop handshake is indicated by a new Link
protocol version.
(We use a Link protocol because they are advertised in the VERSIONS cell at the start of a handshake. Since the client doesn't necessarily have a descriptor for the relay, it can't look at its official protocols list.)
Appendices
Appendix: Reserved values
We must reserve:
- A new HTYPE for use in CREATE2 and EXTEND2
- A new Link subprotocol version to indicate the presence of CreateOnehop.
Appendix: Alternative designs (rejected)
New CREATE_FAST syntax
Instead of a new handshake in CREATE2 cells,
we could define a new syntax for CREATE_FAST cells.
We could indicate support for this syntax
by a new Link
protocol version,
or we could have clients try to use it optimistically.
One reason to do it this way would be to make sure CreateOneHop can't be sent from an EXTEND2 message. But that risk doesn't seem too bad: clients can't put a CreateOneHop in an EXTEND2 accidentally, and once relays are upgraded to follow this spec, they won't obey an EXTEND2 containing a CreateOneHop.
TLS binding (or something) to prevent use in EXTEND2 cells
We could use a TLS key exporter output as part of the client's message, to make sure that a CreateOneHop will never be accepted if it is relayed from an EXTEND2 message.
But again, the risk here doesn't seem too bad.
No encryption with one-hop circuits
The TLS handshake already provides encryption and authentication; there is no real reason to negotiate any keying material on a one-hop circuit, or indeed even to encrypt a one-hop circuit at all.
In the past, however, we've declined to remove negotiation and encryption from one-hop circuits, because it seemed risky to have support at all for unencrypted circuits, and because the overhead of circuit encryption is relatively low.
In-protocol descriptor query
We could provide an in-protocol mechanism for the client to ask for the relay's current onion key (or its whole descriptor). Then, the client could use that key to create a regular circuit via ntor-v3.