研究

2026-03-11 组会

2026-03-11 组会:关于隐藏服务XON/XOFF机制确认与实验方案

Client->RP<-HS链路结构确认

7-hop & 6-node

经过确认,虽然Tor Proposal中写的是HS->RP为三跳电路,但是在实际代码实现当中是

1
HS -> Guard -> Middle1 -> Middle2 -> RP

因此Client->RP<-HS链路的完整结构应当为:

1
Client -> C Guard -> C Middle -> RP <- S Middle2 <- S Middle1 <- S Guard <- HS

依据

在最新的Tor源码实现中提到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// circuitbuild.c

/** Helper for new_route_len(). Choose a circuit length for purpose
* <b>purpose</b>: DEFAULT_ROUTE_LEN (+ 1 if someone else chose the
* exit). If someone else chose the exit, they could be colluding
* with the exit, so add a randomly selected node to preserve
* anonymity.
*
* Here, "exit node" sometimes means an OR acting as an internal
* endpoint, rather than as a relay to an external endpoint. This
* means there need to be at least DEFAULT_ROUTE_LEN routers between
* us and the internal endpoint to preserve the same anonymity
* properties that we would get when connecting to an external
* endpoint. These internal endpoints can include:
*
* - Connections to a directory of hidden services
* (CIRCUIT_PURPOSE_C_GENERAL)
*
* - A client connecting to an introduction point, which the hidden
* service picked (CIRCUIT_PURPOSE_C_INTRODUCING, via
* circuit_get_open_circ_or_launch() which rewrites it from
* CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT)
*
* - A hidden service connecting to a rendezvous point, which the
* client picked (CIRCUIT_PURPOSE_S_CONNECT_REND.
*
* There are currently two situations where we picked the exit node
* ourselves, making DEFAULT_ROUTE_LEN a safe circuit length:
*
* - We are a hidden service connecting to an introduction point
* (CIRCUIT_PURPOSE_S_ESTABLISH_INTRO).
*
* - We are a router testing its own reachabiity
* (CIRCUIT_PURPOSE_TESTING, via router_do_reachability_checks())
*
* onion_pick_cpath_exit() bypasses us (by not calling
* new_route_len()) in the one-hop tunnel case, so we don't need to
* handle that.
*/

对于一些exit不是由隐藏服务端选择的时候,会选择额外加一跳来保证匿名性。这种情况包括了:

  1. Client连接HSDir
  2. Client连接INTRODUCING
  3. HS连接RP

因此HSRP应当是4-hop。

另一方面,从实际的隐藏服务的节点上通过nyx进行监控,可以看到PURPOSEHS_service_introHS_service_rend的电路都是4-hop。

image-20260311140416050

XON/XOFF处理位置

结论

ClientHS的连接当中,XON/XOFF不会在RP的位置被处理,而是被RP转发给HS,由HS进行处理。

依据

在最新的Tor源代码当中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
// relay.c

/** Receive a relay cell:
* - Crypt it (encrypt if headed toward the origin or if we <b>are</b> the
* origin; decrypt if we're headed toward the exit).
* - Check if recognized (if exitward).
* - If recognized and the digest checks out, then find if there's a stream
* that the cell is intended for, and deliver it to the right
* connection_edge.
* - If not recognized, then we need to relay it: append it to the appropriate
* cell_queue on <b>circ</b>.
*
* Return -<b>reason</b> on failure, else 0.
*/
int
circuit_receive_relay_cell(cell_t *cell, circuit_t *circ,
cell_direction_t cell_direction)
{
channel_t *chan = NULL;
crypt_path_t *layer_hint=NULL;
char recognized=0;
int reason;

tor_assert(cell);
tor_assert(circ);
tor_assert(cell_direction == CELL_DIRECTION_OUT ||
cell_direction == CELL_DIRECTION_IN);
if (circ->marked_for_close)
return 0;

if (relay_decrypt_cell(circ, cell, cell_direction, &layer_hint, &recognized)
< 0) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"relay crypt failed. Dropping connection.");
return -END_CIRC_REASON_INTERNAL;
}

circuit_update_channel_usage(circ, cell);

if (recognized) {
edge_connection_t *conn = NULL;
relay_cell_fmt_t format = circuit_get_relay_format(circ, layer_hint);

relay_msg_t msg_buf;
if (relay_msg_decode_cell_in_place(format, cell, &msg_buf) < 0) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Received undecodable relay cell");
return -END_CIRC_REASON_TORPROTOCOL;
}
const relay_msg_t *msg = &msg_buf;

if (circ->purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING) {
if (pathbias_check_probe_response(circ, msg) == -1) {
pathbias_count_valid_cells(circ, msg);
}

/* We need to drop this cell no matter what to avoid code that expects
* a certain purpose (such as the hidserv code). */
return 0;
}

conn = relay_lookup_conn(circ, msg, cell_direction, layer_hint);
if (cell_direction == CELL_DIRECTION_OUT) {
++stats_n_relay_cells_delivered;
log_debug(LD_OR,"Sending away from origin.");
reason = connection_edge_process_relay_cell(msg, circ, conn, NULL);
if (reason < 0) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"connection_edge_process_relay_cell (away from origin) "
"failed.");
return reason;
}
} else if (cell_direction == CELL_DIRECTION_IN) {
++stats_n_relay_cells_delivered;
log_debug(LD_OR,"Sending to origin.");
reason = connection_edge_process_relay_cell(msg, circ, conn,
layer_hint);
if (reason < 0) {
/* If a client is trying to connect to unknown hidden service port,
* END_CIRC_AT_ORIGIN is sent back so we can then close the circuit.
* Do not log warn as this is an expected behavior for a service. */
if (reason != END_CIRC_AT_ORIGIN) {
log_warn(LD_OR,
"connection_edge_process_relay_cell (at origin) failed.");
}
return reason;
}
}
return 0;
}

当节点收到一个cell的时候,先检查是否recognized,如果recognized,那么找到对应的stream然后交付,如果not recognized,那么转发。RP并非streamendpoint,这意味着几乎只承担一个middle的作用。

另一方面,从实际架设的隐藏服务来看,当Client发出一个下载请求并开启方波模式的时候,能看到HSnyx中upload的流量呈现方波模式,并且此RP是随机选择的,这能够证明XON/XOFF的确到达了HS并直接限制了HS的发送速率。

image-20260311154927534

从下图可以看到,此时RP是随机选择的公网Relay,仍然能够达到方波的效果。

image-20260311155110799

实验设计与问题

Conflux机制

nyx的监控和协议来看,HS端是不存在Conflux机制的,HSRP只有条电路,自然就只有1个Guard,因此直接监控即可,不需要合并流量进行分析。

编码设计

暂时有一个用摩斯编码设计一个单词的想法,但是没想好。

平均的速率非常稳定,在XON携带的建议值的0.75-0.78之间,间歇性有峰值接近携带建议值,但是建议值越大越不容易接近,并且一定不会超过建议值。

image-20260311181645094

问题

目前没有Guard节点,节点需要比较长的时间才能转变成Guard