CircMgr: generate a circuit to a random last hop with given properties
For introduction points and rendezvous points, we want to be able to ask the circuit manager for a circuit to a randomly chosen last hop with certain properties. This might be a new usage type. The caller will need to be able to find out what the last hop is, so it can tell the other side of the onion service handshake.
If the caller wants to extend to a specific last hop, then it needs to get a three-hop circuit and extend it to the fourth hop as a final step. (These circuits are four hops since, if somebody else is picking the last hop for us, we can't assume it will give us any anonymity.)
These are single-use circuits. We need to make sure that they obey isolation and family rules.
Current design
I'm planning to add a new pool of 3-hop circuits to the circmgr. This is different from our current list of circuits in that each of these circuits can only be given out by the circmgr in response to a single request.
(We need to use a pool here so that we don't have to incur so much latency when we want a circuit like this.)
One new operation on the circmgr will be: "Give me a 3-hop circuit to an arbitrary final hop, for use as a {rend,intro} point that I selected." This will have to build a circuit or take it from the pool as needed, and return that circuit along with a description of its final hop.
Another new operation on the circmgr will be "Give me a 4-hop circuit to hop X, for use as a {hsdir point,intro point,rend point}." This will have to build a circuit or take it from the pool as needed, ensuring that the circuit uses no relays that share a family with X, and then extend that circuit to X.
Open questions
A separate pool or a new kind of isolation?
I am leaning towards saying that these circuits should not use the same set of open/pending circuits as that used by potentially shared circuits. This would help ensure that we never accidentally share a circuit that's getting used for this kind of request.
As an alternative, I guess we could try to use circuit isolation to ensure that any circuit used in this way gets a fresh isolation token that can never be shared with anything else. I wonder if that's more accident-prone or less.
Detecting family overlap with the target relay X
Our current family detection code assumes we have access to a NetDir
, and happens when we are constructing paths. But now we'll need to run that code as part of our "is this circuit suitable for our purpose?" detection, which does not currently assume access to a NetDir
.
I'm guessing that our best bet here might be to simply pass in a NetDir (or NetDirProvider) to those checks. But I wonder if there isn't a better alternative. I suppose that we could make a list of excluded relay Ids based our NetDir and the target relay X, and then use that list as part of our usage. I wonder if that's any better.
Specifying the target relay X
When we want to extend to a chosen relay X as a rendezvous point or introduction point, we don't have an entry from our NetDir: instead, we have a Vec<UnparsedLinkSpec>
and an onion key (KP_ntor
, if you want).
But our code assumes that every hop of a circuit will be a CircTarget
, which extends ChanTarget
. This means that the CircTarget
must have a set of protovers (which we don't actually know), a ChanMethod
, a set of addresses, and more.
In practice, on today's tor network, every valid target will have RelayIds
, and at least one address. So we could enforce a rule that says we will not use X unless it has at least:
- One identity type we recognize (RSA or Ed)
- One address type we recognize (IPv4 or IPv6)
And we could say that we will not assume that X supports any protovers beyond the set required for all relays. Would that be sufficient?