Add client-side onion service circuit hiding machines.

This commit is contained in:
George Kadianakis
2019-05-16 14:02:16 +03:00
parent 9b582edddb
commit ac895fa405
6 changed files with 662 additions and 4 deletions
+2
View File
@@ -37,6 +37,7 @@ LIBTOR_APP_A_SOURCES = \
src/core/or/circuitmux.c \
src/core/or/circuitmux_ewma.c \
src/core/or/circuitpadding.c \
src/core/or/circuitpadding_machines.c \
src/core/or/circuitstats.c \
src/core/or/circuituse.c \
src/core/or/crypt_path.c \
@@ -247,6 +248,7 @@ noinst_HEADERS += \
src/core/or/circuitmux_ewma.h \
src/core/or/circuitstats.h \
src/core/or/circuitpadding.h \
src/core/or/circuitpadding_machines.h \
src/core/or/circuituse.h \
src/core/or/command.h \
src/core/or/connection_edge.h \
+14 -4
View File
@@ -53,6 +53,7 @@
#include "lib/math/prob_distr.h"
#include "core/or/or.h"
#include "core/or/circuitpadding.h"
#include "core/or/circuitpadding_machines.h"
#include "core/or/circuitlist.h"
#include "core/or/circuituse.h"
#include "core/mainloop/netstatus.h"
@@ -79,8 +80,6 @@
#include "app/config/config.h"
static inline circpad_purpose_mask_t circpad_circ_purpose_to_mask(uint8_t
circ_purpose);
static inline circpad_circuit_state_t circpad_circuit_state(
origin_circuit_t *circ);
static void circpad_setup_machine_on_circ(circuit_t *on_circ,
@@ -492,7 +491,10 @@ circpad_machine_setup_tokens(circpad_machine_runtime_t *mi)
const circpad_state_t *state = circpad_machine_current_state(mi);
/* If this state doesn't exist, or doesn't have token removal,
* free any previous state's histogram, and bail */
* free any previous state's runtime histogram, and bail.
*
* If we don't have a token removal strategy, we also don't need a runtime
* histogram and we rely on the immutable one in machine_spec_t. */
if (!state || state->token_removal == CIRCPAD_TOKEN_REMOVAL_NONE) {
if (mi->histogram) {
tor_free(mi->histogram);
@@ -1990,7 +1992,6 @@ circpad_circuit_state(origin_circuit_t *circ)
* Convert a normal circuit purpose into a bitmask that we can
* use for determining matching circuits.
*/
static inline
circpad_purpose_mask_t
circpad_circ_purpose_to_mask(uint8_t circ_purpose)
{
@@ -2623,6 +2624,14 @@ circpad_machines_init(void)
origin_padding_machines = smartlist_new();
relay_padding_machines = smartlist_new();
/* Register machines for hiding client-side intro circuits */
circpad_machine_client_hide_intro_circuits(origin_padding_machines);
circpad_machine_relay_hide_intro_circuits(relay_padding_machines);
/* Register machines for hiding client-side rendezvous circuits */
circpad_machine_client_hide_rend_circuits(origin_padding_machines);
circpad_machine_relay_hide_rend_circuits(relay_padding_machines);
// TODO: Parse machines from consensus and torrc
#ifdef TOR_UNIT_TESTS
circpad_circ_client_machine_init();
@@ -2877,6 +2886,7 @@ circpad_handle_padding_negotiated(circuit_t *circ, cell_t *cell,
}
if (negotiated->command == CIRCPAD_COMMAND_STOP) {
log_info(LD_CIRC, "Received STOP command on PADDING_NEGOTIATED");
/* There may not be a padding_info here if we shut down the
* machine in circpad_shutdown_old_machines(). Or, if
* circpad_add_matching_matchines() added a new machine,
+5
View File
@@ -603,6 +603,9 @@ typedef uint8_t circpad_machine_num_t;
/** Global state machine structure from the consensus */
typedef struct circpad_machine_spec_t {
/* Just a user-friendly machine name for logs */
const char *name;
/** Global machine number */
circpad_machine_num_t machine_num;
@@ -728,6 +731,8 @@ bool circpad_padding_negotiated(struct circuit_t *circ,
uint8_t command,
uint8_t response);
circpad_purpose_mask_t circpad_circ_purpose_to_mask(uint8_t circ_purpose);
MOCK_DECL(circpad_decision_t,
circpad_machine_schedule_padding,(circpad_machine_runtime_t *));
+418
View File
@@ -0,0 +1,418 @@
/* Copyright (c) 2019 The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file circuitpadding_machines.c
* \brief Circuit padding state machines
*
* \detail
*
* Introduce circuit padding machines that will be used by Tor circuits, as
* specified by proposal 302 "Hiding onion service clients using padding".
*
* Right now this file introduces two machines that aim to hide the client-side
* of onion service circuits against naive classifiers like the ones from the
* "Circuit Fingerprinting Attacks: Passive Deanonymization of Tor Hidden
* Services" paper from USENIX. By naive classifiers we mean classifiers that
* use basic features like "circuit construction circuits" and "incoming and
* outgoing cell counts" and "duration of activity".
*
* In particular, these machines aim to be lightweight and protect against
* these basic classifiers. They don't aim to protect against more advanced
* attacks that use deep learning or even correlate various circuit
* construction events together. Machines that fool such advanced classifiers
* are also possible, but they can't be so lightweight and might require more
* WTF-PAD features. So for now we opt for the following two machines:
*
* Client-side introduction circuit hiding machine:
*
* This machine hides client-side introduction circuits by making their
* circuit consruction sequence look like normal general circuits that
* download directory information. Furthermore, the circuits are kept open
* until all the padding has been sent, since intro circuits are usually
* very short lived and this act as a distinguisher. For more info see
* circpad_machine_client_hide_intro_circuits() and the sec.
*
* Client-side rendezvous circuit hiding machine:
*
* This machine hides client-side rendezvous circuits by making their
* circuit construction sequence look like normal general circuits. For more
* details see circpad_machine_client_hide_rend_circuits() and the spec.
*
* TODO: These are simple machines that carefully manipulate the cells of the
* initial circuit setup procedure to make them look like general
* circuits. In the future, more states can be baked into their state machine
* to do more advanced obfuscation.
**/
#define CIRCUITPADDING_MACHINES_PRIVATE
#include "core/or/or.h"
#include "feature/nodelist/networkstatus.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "core/or/circuitlist.h"
#include "core/or/circuitpadding_machines.h"
#include "core/or/circuitpadding.h"
/* Setup the simple state machine we use for all HS padding machines */
static void
setup_state_machine_for_hiding_intro_circuits(circpad_machine_spec_t *machine)
{
/* Two states: START, OBFUSCATE_CIRC_SETUP (and END) */
circpad_machine_states_init(machine, 2);
/* For the relay-side machine, we want to transition
* START -> OBFUSCATE_CIRC_SETUP upon first non-padding
* cell sent (PADDING_NEGOTIATED in this case).
*
* For the origin-side machine, we transition to OBFUSCATE_CIRC_SETUP after
* sending PADDING_NEGOTIATE, and we stay there (without sending any padding)
* until we receive a STOP from the other side. */
machine->states[CIRCPAD_STATE_START].
next_state[CIRCPAD_EVENT_NONPADDING_SENT] =
CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP;
/* For the relay-side, we want to transition from OBFUSCATE_CIRC_SETUP to END
* state when the length finishes.
*
* For the origin-side, we don't care because the relay-side machine is gonna
* END us. */
machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
next_state[CIRCPAD_EVENT_LENGTH_COUNT] = CIRCPAD_STATE_END;
/* Now let's define the OBF -> OBF transitions that maintain our padding
* flow:
*
* For the relay-side machine, we want to keep on sending padding bytes even
* when nothing else happens on this circuit. */
machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
next_state[CIRCPAD_EVENT_PADDING_SENT] =
CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP;
/* For the relay-side machine, we need this transition so that we re-enter
the state, after PADDING_NEGOTIATED is sent. Otherwise, the remove token
function will disable the timer, and nothing will restart it since there
is no other motion on an intro circuit. */
machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
next_state[CIRCPAD_EVENT_NONPADDING_SENT] =
CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP;
}
/* Setup the OBFUSCATE_CIRC_SETUP state of the machine that hides client-side
* intro circuits. */
static void
setup_obf_state_for_hiding_intro_circuits(circpad_state_t *obf_state,
bool is_client)
{
/* Token removal strategy for OBFUSCATE_CIRC_SETUP state. We pick the
* simplest one since we rely on the state length sampling and not the
* tokens. */
obf_state->token_removal = CIRCPAD_TOKEN_REMOVAL_NONE;
/* Figure out the length of the OBFUSCATE_CIRC_SETUP state so that it's
* randomized. We will set the histogram such that we don't send any padding
* from the origin-side, so just tune these parameteres for the
* relay-side. */
obf_state->length_dist.type = CIRCPAD_DIST_UNIFORM;
obf_state->length_dist.param1 = INTRO_MACHINE_MINIMUM_PADDING;
obf_state->length_dist.param2 = INTRO_MACHINE_MAXIMUM_PADDING;
/* Configure histogram */
obf_state->histogram_len = 2;
if (is_client) {
/* For the origin-side machine we don't want to send any padding, so setup
* infinite delays. */
obf_state->histogram_edges[0] = CIRCPAD_DELAY_INFINITE-1;
obf_state->histogram_edges[1] = CIRCPAD_DELAY_INFINITE;
/* zero tokens */
obf_state->histogram[0] = 0;
} else {
/* For the relay-side machine we want to batch padding instantly to pretend
* its an incoming directory download. So set the histogram edges tight:
* (1, 10ms, infinity). */
obf_state->histogram_edges[0] = 1000;
obf_state->histogram_edges[1] = 10000;
/* padding is controlled by state length, so this is just a high value */
obf_state->histogram[0] = 1000;
}
/* just one bin, so setup the total tokens */
obf_state->histogram_total_tokens = obf_state->histogram[0];
}
/** Create a client-side padding machine that aims to hide IP circuits. In
* particular, it keeps intro circuits alive until a bunch of fake traffic has
* been pushed through.
*/
void
circpad_machine_client_hide_intro_circuits(smartlist_t *machines_sl)
{
circpad_machine_spec_t *client_machine
= tor_malloc_zero(sizeof(circpad_machine_spec_t));
client_machine->name = "client_ip_circ";
client_machine->conditions.state_mask = CIRCPAD_CIRC_OPENED;
client_machine->target_hopnum = 2;
/* This is a client machine */
client_machine->is_origin_side = 1;
/* We only want to pad introduction circuits, and we want to start padding
* only after the INTRODUCE1 cell has been sent, so set the purposes
* appropriately.
*
* In particular we want introduction circuits to blend as much as possible
* with general circuits. Most general circuits have the following initial
* relay cell sequence (outgoing cells marked in [brackets]):
*
* [EXTEND2] -> EXTENDED2 -> [EXTEND2] -> EXTENDED2 -> [BEGIN] -> CONNECTED
* -> [DATA] -> [DATA] -> DATA -> DATA...(inbound data cells continue)
*
* Whereas normal introduction circuits usually look like:
*
* [EXTEND2] -> EXTENDED2 -> [EXTEND2] -> EXTENDED2 -> [EXTEND2] -> EXTENDED2
* -> [INTRO1] -> INTRODUCE_ACK
*
* This means that up to the sixth cell (first line of each sequence above),
* both general and intro circuits have identical cell sequences. After that
* we want to mimic the second line sequence of
* -> [DATA] -> [DATA] -> DATA -> DATA...(inbound data cells continue)
*
* We achieve this by starting padding INTRODUCE1 has been sent. With padding
* negotiation cells, in the common case of the second line looks like:
* -> [INTRO1] -> [PADDING_NEGOTIATE] -> PADDING_NEGOTIATED -> INTRO_ACK
*
* Then, the middle node will send between INTRO_MACHINE_MINIMUM_PADDING and
* INTRO_MACHINE_MAXIMUM_PADDING cells, to match the "...(inbound data cells
* continue)" portion of the trace (aka the rest of an HTTPS response body).
*/
client_machine->conditions.purpose_mask =
circpad_circ_purpose_to_mask(CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT)|
circpad_circ_purpose_to_mask(CIRCUIT_PURPOSE_C_INTRODUCE_ACKED)|
circpad_circ_purpose_to_mask(CIRCUIT_PURPOSE_C_CIRCUIT_PADDING);
/* Keep the circuit alive even after the introduction has been finished,
* otherwise the short-term lifetime of the circuit will blow our cover */
client_machine->manage_circ_lifetime = 1;
/* Set padding machine limits to help guard against excessive padding */
client_machine->allowed_padding_count = INTRO_MACHINE_MAXIMUM_PADDING;
client_machine->max_padding_percent = 1;
/* Setup states and histograms */
setup_state_machine_for_hiding_intro_circuits(client_machine);
setup_obf_state_for_hiding_intro_circuits(
&client_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP],
true);
/* Register the machine */
client_machine->machine_num = smartlist_len(machines_sl);
circpad_register_padding_machine(client_machine, machines_sl);
log_warn(LD_GENERAL,
"Registered client intro point hiding padding machine (%u)",
client_machine->machine_num);
}
/** Create a relay-side padding machine that aims to hide IP circuits. See
* comments on the function above for more details on the workings of the
* machine. */
void
circpad_machine_relay_hide_intro_circuits(smartlist_t *machines_sl)
{
circpad_machine_spec_t *relay_machine
= tor_malloc_zero(sizeof(circpad_machine_spec_t));
relay_machine->name = "relay_ip_circ";
relay_machine->conditions.state_mask = CIRCPAD_CIRC_OPENED;
relay_machine->target_hopnum = 2;
/* This is a relay-side machine */
relay_machine->is_origin_side = 0;
/* We want to negotiate END from this side after all our padding is done, so
* that the origin-side machine goes into END state, and eventually closes
* the circuit. */
relay_machine->should_negotiate_end = 1;
/* Set padding machine limits to help guard against excessive padding */
relay_machine->allowed_padding_count = INTRO_MACHINE_MAXIMUM_PADDING;
relay_machine->max_padding_percent = 1;
/* Setup states and histograms */
setup_state_machine_for_hiding_intro_circuits(relay_machine);
setup_obf_state_for_hiding_intro_circuits(
&relay_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP],
false);
/* Register the machine */
relay_machine->machine_num = smartlist_len(machines_sl);
circpad_register_padding_machine(relay_machine, machines_sl);
log_warn(LD_GENERAL,
"Registered relay intro circuit hiding padding machine (%u)",
relay_machine->machine_num);
}
/************************** Rendezvous-circuit machine ***********************/
/* Setup the obf state of the machine that hides client-side rend
* circuits. */
static void
setup_obf_state_for_hiding_rend_circuits(circpad_state_t *obf_state)
{
/* Don't use a token removal strategy since we don't want to use monotime
functions. We want this machine to be light. */
obf_state->token_removal = CIRCPAD_TOKEN_REMOVAL_NONE;
/* Instead, to control the volume of padding (we just want to send a single
* padding cell) we will use a static state length. We just want one token,
* since we want to make the following pattern:
* [PADDING_NEGOTIATE] -> [DROP] -> PADDING_NEGOTIATED -> DROP */
obf_state->length_dist.type = CIRCPAD_DIST_UNIFORM;
obf_state->length_dist.param1 = 1;
obf_state->length_dist.param2 = 2;
/* Histogram is: (0 msecs, 50 msecs, infinity). We want this to be fast so
* that the incoming PADDING_NEGOTIATED cell always arrives after the
* outgoing [DROP]. */
obf_state->histogram_len = 2;
obf_state->histogram_edges[0] = 0;
obf_state->histogram_edges[1] = 1000;
/* dummy amount of tokens. they don't matter. we rely on state length. */
obf_state->histogram[0] = 1;
obf_state->histogram_total_tokens = 1;
}
/* Setup the simple state machine we use for all HS padding machines */
static void
setup_state_machine_for_hiding_rend_circuits(circpad_machine_spec_t *machine)
{
/* Two states: START, OBFUSCATE_CIRC_SETUP (and END) */
circpad_machine_states_init(machine, 2);
/* START -> OBFUSCATE_CIRC_SETUP transition upon sending the first
* non-padding cell (which is PADDING_NEGOTIATE) */
machine->states[CIRCPAD_STATE_START].
next_state[CIRCPAD_EVENT_NONPADDING_SENT] =
CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP;
/* OBFUSCATE_CIRC_SETUP -> END transition when we finish all the tokens */
machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
next_state[CIRCPAD_EVENT_PADDING_RECV] = CIRCPAD_STATE_END;
machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP].
next_state[CIRCPAD_EVENT_LENGTH_COUNT] = CIRCPAD_STATE_END;
}
/** Create a client-side padding machine that aims to hide rendezvous
* circuits.*/
void
circpad_machine_client_hide_rend_circuits(smartlist_t *machines_sl)
{
circpad_machine_spec_t *client_machine
= tor_malloc_zero(sizeof(circpad_machine_spec_t));
client_machine->name = "client_rp_circ";
/* Only pad after the circuit has been built and pad to the middle */
client_machine->conditions.state_mask = CIRCPAD_CIRC_OPENED;
client_machine->target_hopnum = 2;
/* This is a client machine */
client_machine->is_origin_side = 1;
/* We only want to pad rendezvous circuits, and we want to start padding only
* after the rendezvous circuit has been established.
*
* Following a similar argument as for intro circuits, we are aiming for
* padded rendezvous circuits to blend in with the initial cell sequence of
* general circuits which usually look like this:
*
* [EXTEND2] -> EXTENDED2 -> [EXTEND2] -> EXTENDED2 -> [BEGIN] -> CONNECTED
* -> [DATA] -> [DATA] -> DATA -> DATA...(incoming cells continue)
*
* Whereas normal rendezvous circuits usually look like:
*
* [EXTEND2] -> EXTENDED2 -> [EXTEND2] -> EXTENDED2 -> [EST_REND] -> REND_EST
* -> REND2 -> [BEGIN]
*
* This means that up to the sixth cell (in the first line), both general and
* rend circuits have identical cell sequences.
*
* After that we want to mimic a [DATA] -> [DATA] -> DATA -> DATA sequence.
*
* With padding negotiation right after the REND_ESTABLISHED, the sequence
* becomes:
*
* [EXTEND2] -> EXTENDED2 -> [EXTEND2] -> EXTENDED2 -> [EST_REND] -> REND_EST
* -> [PADDING_NEGOTIATE] -> [DROP] -> PADDING_NEGOTIATED -> DROP...
*
* After which normal application DATA cells continue on the circuit.
*
* Hence this way we make rendezvous circuits look like general circuits up
* till the end of the circuit setup. */
client_machine->conditions.purpose_mask =
circpad_circ_purpose_to_mask(CIRCUIT_PURPOSE_C_REND_JOINED)|
circpad_circ_purpose_to_mask(CIRCUIT_PURPOSE_C_REND_READY)|
circpad_circ_purpose_to_mask(CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED);
/* Set padding machine limits to help guard against excessive padding */
client_machine->allowed_padding_count = 1;
client_machine->max_padding_percent = 1;
/* Setup states and histograms */
setup_state_machine_for_hiding_rend_circuits(client_machine);
setup_obf_state_for_hiding_rend_circuits(
&client_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP]);
/* Register the machine */
client_machine->machine_num = smartlist_len(machines_sl);
circpad_register_padding_machine(client_machine, machines_sl);
log_warn(LD_GENERAL,
"Registered client rendezvous circuit hiding padding machine (%u)",
client_machine->machine_num);
}
/** Create a relay-side padding machine that aims to hide IP circuits.
*
* This is meant to follow the client-side machine.
*/
void
circpad_machine_relay_hide_rend_circuits(smartlist_t *machines_sl)
{
circpad_machine_spec_t *relay_machine
= tor_malloc_zero(sizeof(circpad_machine_spec_t));
relay_machine->name = "relay_rp_circ";
/* Only pad after the circuit has been built and pad to the middle */
relay_machine->conditions.min_hops = 2;
relay_machine->conditions.state_mask = CIRCPAD_CIRC_OPENED;
relay_machine->target_hopnum = 2;
/* This is a relay-side machine */
relay_machine->is_origin_side = 0;
/* Set padding machine limits to help guard against excessive padding */
relay_machine->allowed_padding_count = 1;
relay_machine->max_padding_percent = 1;
/* Setup states and histograms */
setup_state_machine_for_hiding_rend_circuits(relay_machine);
setup_obf_state_for_hiding_rend_circuits(
&relay_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP]);
/* Register the machine */
relay_machine->machine_num = smartlist_len(machines_sl);
circpad_register_padding_machine(relay_machine, machines_sl);
log_warn(LD_GENERAL,
"Registered relay rendezvous circuit hiding padding machine (%u)",
relay_machine->machine_num);
}
+35
View File
@@ -0,0 +1,35 @@
/* Copyright (c) 2018 The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file circuitpadding_machines.h
* \brief Header file for circuitpadding_machines.c.
**/
#ifndef TOR_CIRCUITPADDING_MACHINES_H
#define TOR_CIRCUITPADDING_MACHINES_H
void circpad_machine_relay_hide_intro_circuits(smartlist_t *machines_sl);
void circpad_machine_client_hide_intro_circuits(smartlist_t *machines_sl);
void circpad_machine_relay_hide_rend_circuits(smartlist_t *machines_sl);
void circpad_machine_client_hide_rend_circuits(smartlist_t *machines_sl);
#ifdef CIRCUITPADDING_MACHINES_PRIVATE
/** State of the padding machines that actually sends padding */
#define CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP CIRCPAD_STATE_BURST
/** Constants defining the amount of padding that a machine will send to hide
* HS circuits. The actual value is sampled uniformly random between the
* min/max values.
*/
/** Minimum number of relay-side padding cells to be sent by this machine */
#define INTRO_MACHINE_MINIMUM_PADDING 7
/** Maximum number of relay-side padding cells to be sent by this machine.
* The actual value will be sampled between the min and max.*/
#define INTRO_MACHINE_MAXIMUM_PADDING 10
#endif
#endif
+188
View File
@@ -415,6 +415,8 @@ helper_create_basic_machine(void)
/* Start, burst */
circpad_machine_states_init(&circ_client_machine, 2);
circ_client_machine.name = "basic";
circ_client_machine.states[CIRCPAD_STATE_START].
next_state[CIRCPAD_EVENT_NONPADDING_RECV] = CIRCPAD_STATE_BURST;
circ_client_machine.states[CIRCPAD_STATE_START].use_rtt_estimate = 1;
@@ -2917,6 +2919,192 @@ test_circuitpadding_manage_circuit_lifetime(void *arg)
UNMOCK(tor_gettimeofday);
}
/** Helper for the test_circuitpadding_hs_machines test:
*
* - Create a client and relay circuit.
* - Setup right circuit purpose and attach a machine to the client circuit.
* - Verify that state transitions work as intended and state length gets
* enforced.
*
* This function is able to do this test both for intro and rend circuits
* depending on the value of <b>test_intro_circs</b>.
*/
static void
helper_test_hs_machines(bool test_intro_circs)
{
/* Setup the circuits */
origin_circuit_t *origin_client_side = origin_circuit_new();
client_side = TO_CIRCUIT(origin_client_side);
client_side->purpose = CIRCUIT_PURPOSE_C_GENERAL;
dummy_channel.cmux = circuitmux_alloc();
relay_side = TO_CIRCUIT(new_fake_orcirc(&dummy_channel, &dummy_channel));
relay_side->purpose = CIRCUIT_PURPOSE_OR;
/* extend the client circ to two hops */
simulate_single_hop_extend(client_side, relay_side, 1);
simulate_single_hop_extend(client_side, relay_side, 1);
/* machines only apply on opened circuits */
origin_client_side->has_opened = 1;
/************************************/
/* Attaching the client machine now won't work here because of a wrong
* purpose */
tt_assert(!client_side->padding_machine[0]);
circpad_add_matching_machines(origin_client_side, origin_padding_machines);
tt_assert(!client_side->padding_machine[0]);
/* Change the purpose, see the machine getting attached */
client_side->purpose = test_intro_circs ?
CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT : CIRCUIT_PURPOSE_C_REND_JOINED;
circpad_add_matching_machines(origin_client_side, origin_padding_machines);
tt_ptr_op(client_side->padding_info[0], OP_NE, NULL);
tt_ptr_op(client_side->padding_machine[0], OP_NE, NULL);
tt_ptr_op(relay_side->padding_info[0], OP_NE, NULL);
tt_ptr_op(relay_side->padding_machine[0], OP_NE, NULL);
/* Verify that the right machine is attached */
tt_str_op(client_side->padding_machine[0]->name, OP_EQ,
test_intro_circs ? "client_ip_circ" : "client_rp_circ");
tt_str_op(relay_side->padding_machine[0]->name, OP_EQ,
test_intro_circs ? "relay_ip_circ": "relay_rp_circ");
/***********************************/
/* Intro machines are at START state, but rend machines have already skipped
* to OBFUSCATE_CIRC_SETUP because of the sent PADDING_NEGOTIATE. */
tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP);
tt_int_op(relay_side->padding_info[0]->current_state, OP_EQ,
CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP);
/*Send non-padding to move the machines from START to OBFUSCATE_CIRC_SETUP */
circpad_cell_event_nonpadding_received(client_side);
circpad_cell_event_nonpadding_received(relay_side);
tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP);
tt_int_op(relay_side->padding_info[0]->current_state, OP_EQ,
CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP);
/* For rendezvous circuit machines we can stop early since are simpler than
* the intro circuit machines. */
if (!test_intro_circs) {
tt_int_op(client_side->padding_info[0]->histogram[0], OP_EQ, 1);
goto done;
}
/* Check that the state lengths have been sampled and are within range */
circpad_machine_runtime_t *client_machine_runtime =
client_side->padding_info[0];
circpad_machine_runtime_t *relay_machine_runtime =
relay_side->padding_info[0];
if (test_intro_circs) {
tt_int_op(client_machine_runtime->state_length, OP_GE,
INTRO_MACHINE_MINIMUM_PADDING);
tt_int_op(client_machine_runtime->state_length, OP_LT,
INTRO_MACHINE_MAXIMUM_PADDING);
tt_int_op(relay_machine_runtime->state_length, OP_GE,
INTRO_MACHINE_MINIMUM_PADDING);
tt_int_op(relay_machine_runtime->state_length, OP_LT,
INTRO_MACHINE_MAXIMUM_PADDING);
} else {
tt_int_op(client_machine_runtime->state_length, OP_EQ, 1);
tt_int_op(relay_machine_runtime->state_length, OP_EQ, 1);
}
/* Send state_length worth of padding and see that the state goes to END */
int i;
for (i = (int) client_machine_runtime->state_length ; i > 0 ; i--) {
circpad_send_padding_cell_for_callback(client_machine_runtime);
}
/* See that the machine has been teared down after all the length has been
* exhausted. */
tt_int_op(client_side->padding_info[0]->current_state, OP_EQ,
CIRCPAD_STATE_END);
done:
free_fake_orcirc(relay_side);
circuitmux_detach_all_circuits(dummy_channel.cmux, NULL);
circuitmux_free(dummy_channel.cmux);
free_fake_origin_circuit(TO_ORIGIN_CIRCUIT(client_side));
}
/** Test that the HS circuit padding machines work as intended. */
static void
test_circuitpadding_hs_machines(void *arg)
{
(void)arg;
/* Test logic:
*
* 1) Register the HS machines, which aim to hide the presense of
* onion service traffic on the client-side
*
* 2) Call helper_test_hs_machines() to perform tests for the intro circuit
* machines and for the rend circuit machines.
*/
MOCK(circuitmux_attach_circuit, circuitmux_attach_circuit_mock);
MOCK(circuit_package_relay_cell, circuit_package_relay_cell_mock);
MOCK(circuit_get_nth_node, circuit_get_nth_node_mock);
MOCK(circpad_machine_schedule_padding,circpad_machine_schedule_padding_mock);
origin_padding_machines = smartlist_new();
relay_padding_machines = smartlist_new();
nodes_init();
monotime_init();
monotime_enable_test_mocking();
monotime_set_mock_time_nsec(1*TOR_NSEC_PER_USEC);
monotime_coarse_set_mock_time_nsec(1*TOR_NSEC_PER_USEC);
curr_mocked_time = 1*TOR_NSEC_PER_USEC;
timers_initialize();
/* This is needed so that we are not considered to be dormant */
note_user_activity(20);
/************************************/
/* Register the HS machines */
circpad_machine_client_hide_intro_circuits(origin_padding_machines);
circpad_machine_client_hide_rend_circuits(origin_padding_machines);
circpad_machine_relay_hide_intro_circuits(relay_padding_machines);
circpad_machine_relay_hide_rend_circuits(relay_padding_machines);
/***********************************/
/* Do the tests for the intro circuit machines */
helper_test_hs_machines(true);
/* Do the tests for the rend circuit machines */
helper_test_hs_machines(false);
timers_shutdown();
monotime_disable_test_mocking();
SMARTLIST_FOREACH_BEGIN(origin_padding_machines,
circpad_machine_spec_t *, m) {
machine_spec_free(m);
} SMARTLIST_FOREACH_END(m);
SMARTLIST_FOREACH_BEGIN(relay_padding_machines,
circpad_machine_spec_t *, m) {
machine_spec_free(m);
} SMARTLIST_FOREACH_END(m);
smartlist_free(origin_padding_machines);
smartlist_free(relay_padding_machines);
UNMOCK(circuitmux_attach_circuit);
UNMOCK(circuit_package_relay_cell);
UNMOCK(circuit_get_nth_node);
UNMOCK(circpad_machine_schedule_padding);
}
#define TEST_CIRCUITPADDING(name, flags) \
{ #name, test_##name, (flags), NULL, NULL }