summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2019-05-17 11:03:16 -0400
committerGeorge Kadianakis <desnacked@riseup.net>2019-05-27 14:20:07 +0300
commit0bc1241494a118d5319207a9f4683b993d389e77 (patch)
tree64d010086cf5f330dd1fcceaf9ad39b9364b6a09 /src
parent530d1179ffe54ad0db2678142154fdd20f71cf53 (diff)
downloadtor-0bc1241494a118d5319207a9f4683b993d389e77.tar.gz
tor-0bc1241494a118d5319207a9f4683b993d389e77.zip
Make sure that we send at least some random data in RELAY_DATA cells
Proposal 289 prevents SENDME-flooding by requiring the other side to authenticate the data it has received. But this data won't actually be random if they are downloading a known resource. "No problem", we said, "let's fell the empty parts of our cells with some randomness!" and we did that in #26871. Unfortunately, if the relay data payloads are all completely full, there won't be any empty parts for us to randomize. Therefore, we now pick random "randomness windows" between CIRCWINDOW_INCREMENT/2 and CIRCWINDOW_INCREMENT. We remember whether we have sent a cell containing at least 16 bytes of randomness in that window. If we haven't, then when the window is exhausted, we send one. (This window approach is designed to lower the number of rng checks we have to do. The number 16 is pulled out of a hat to change the attacker's guessing difficulty to "impossible".) Implements 28646.
Diffstat (limited to 'src')
-rw-r--r--src/core/or/circuit_st.h11
-rw-r--r--src/core/or/circuitlist.c1
-rw-r--r--src/core/or/circuitlist.h2
-rw-r--r--src/core/or/relay.c76
-rw-r--r--src/core/or/relay.h1
5 files changed, 83 insertions, 8 deletions
diff --git a/src/core/or/circuit_st.h b/src/core/or/circuit_st.h
index 499bf93d6b..3c7b931614 100644
--- a/src/core/or/circuit_st.h
+++ b/src/core/or/circuit_st.h
@@ -92,6 +92,10 @@ struct circuit_t {
/** True iff this circuit has received a DESTROY cell in either direction */
unsigned int received_destroy : 1;
+ /** True iff we have sent a sufficiently random data cell since last
+ * we reset send_randomness_after_n_cells. */
+ unsigned int have_sent_sufficiently_random_cell : 1;
+
uint8_t state; /**< Current status of this circuit. */
uint8_t purpose; /**< Why are we creating this circuit? */
@@ -104,6 +108,13 @@ struct circuit_t {
* circuit-level sendme cells to indicate that we're willing to accept
* more. */
int deliver_window;
+ /**
+ * How many cells do we have until we need to send one that contains
+ * sufficient randomness? Used to ensure that authenticated SENDME cells
+ * will reflect some unpredictable information.
+ **/
+ uint16_t send_randomness_after_n_cells;
+
/** FIFO containing the digest of the cells that are just before a SENDME is
* sent by the client. It is done at the last cell before our package_window
* goes down to 0 which is when we expect a SENDME.
diff --git a/src/core/or/circuitlist.c b/src/core/or/circuitlist.c
index 72952a8a52..ebbe7f0824 100644
--- a/src/core/or/circuitlist.c
+++ b/src/core/or/circuitlist.c
@@ -993,6 +993,7 @@ init_circuit_base(circuit_t *circ)
circ->package_window = circuit_initial_package_window();
circ->deliver_window = CIRCWINDOW_START;
+ circuit_reset_sendme_randomness(circ);
cell_queue_init(&circ->n_chan_cells);
smartlist_add(circuit_get_global_list(), circ);
diff --git a/src/core/or/circuitlist.h b/src/core/or/circuitlist.h
index 6f5fce4875..80c1f7ac4e 100644
--- a/src/core/or/circuitlist.h
+++ b/src/core/or/circuitlist.h
@@ -218,7 +218,7 @@ void circuit_mark_all_dirty_circs_as_unusable(void);
void circuit_synchronize_written_or_bandwidth(const circuit_t *c,
circuit_channel_direction_t dir);
MOCK_DECL(void, circuit_mark_for_close_, (circuit_t *circ, int reason,
- int line, const char *file));
+ int line, const char *cfile));
int circuit_get_cpath_len(origin_circuit_t *circ);
int circuit_get_cpath_opened_len(const origin_circuit_t *);
void circuit_clear_cpath(origin_circuit_t *circ);
diff --git a/src/core/or/relay.c b/src/core/or/relay.c
index 7a121780af..c48147dff8 100644
--- a/src/core/or/relay.c
+++ b/src/core/or/relay.c
@@ -533,6 +533,10 @@ relay_command_to_string(uint8_t command)
}
}
+/** When padding a cell with randomness, leave this many zeros after the
+ * payload. */
+#define CELL_PADDING_GAP 4
+
/** Return the offset where the padding should start. The <b>data_len</b> is
* the relay payload length expected to be put in the cell. It can not be
* bigger than RELAY_PAYLOAD_SIZE else this function assert().
@@ -556,7 +560,7 @@ get_pad_cell_offset(size_t data_len)
/* If the offset is larger than the cell payload size, we return an offset
* of zero indicating that no padding needs to be added. */
- size_t offset = RELAY_HEADER_SIZE + data_len + 4;
+ size_t offset = RELAY_HEADER_SIZE + data_len + CELL_PADDING_GAP;
if (offset >= CELL_PAYLOAD_SIZE) {
return 0;
}
@@ -2028,26 +2032,81 @@ uint64_t stats_n_data_cells_received = 0;
uint64_t stats_n_data_bytes_received = 0;
/**
+ * Called when initializing a circuit, or when we have reached the end of the
+ * window in which we need to send some randomness so that incoming sendme
+ * cells will be unpredictable. Resets the flags and picks a new window.
+ */
+void
+circuit_reset_sendme_randomness(circuit_t *circ)
+{
+ circ->have_sent_sufficiently_random_cell = 0;
+ circ->send_randomness_after_n_cells = CIRCWINDOW_INCREMENT / 2 +
+ crypto_fast_rng_get_uint(get_thread_fast_rng(), CIRCWINDOW_INCREMENT / 2);
+}
+
+/**
+ * Any relay data payload containing fewer than this many real bytes is
+ * considered to have enough randomness to.
+ **/
+#define RELAY_PAYLOAD_LENGTH_FOR_RANDOM_SENDMES \
+ (RELAY_PAYLOAD_SIZE - CELL_PADDING_GAP - 16)
+
+/**
* Helper. Return the number of bytes that should be put into a cell from a
* given edge connection on which <b>n_available</b> bytes are available.
*/
static size_t
connection_edge_get_inbuf_bytes_to_package(size_t n_available,
- int package_partial)
+ int package_partial,
+ circuit_t *on_circuit)
{
if (!n_available)
return 0;
- size_t length = RELAY_PAYLOAD_SIZE;
+ /* Do we need to force this payload to have space for randomness? */
+ const bool force_random_bytes =
+ (on_circuit->send_randomness_after_n_cells == 0) &&
+ (! on_circuit->have_sent_sufficiently_random_cell);
- if (n_available < length) { /* not a full payload available */
+ /* At most how much would we like to send in this cell? */
+ size_t target_length;
+ if (force_random_bytes) {
+ target_length = RELAY_PAYLOAD_LENGTH_FOR_RANDOM_SENDMES;
+ } else {
+ target_length = RELAY_PAYLOAD_SIZE;
+ }
+
+ /* Decide how many bytes we will actually put into this cell. */
+ size_t package_length;
+ if (n_available >= target_length) { /* A full payload is available. */
+ package_length = target_length;
+ } else { /* not a full payload available */
if (package_partial)
- length = n_available; /* just take whatever's available now */
+ package_length = n_available; /* just take whatever's available now */
else
return 0; /* nothing to do until we have a full payload */
}
- return length;
+ /* If we reach this point, we will be definitely sending the cell. */
+ tor_assert_nonfatal(package_length > 0);
+
+ if (package_length <= RELAY_PAYLOAD_LENGTH_FOR_RANDOM_SENDMES) {
+ /* This cell will have enough randomness in the padding to make a future
+ * sendme cell unpredictable. */
+ on_circuit->have_sent_sufficiently_random_cell = 1;
+ }
+
+ if (on_circuit->send_randomness_after_n_cells == 0) {
+ /* Either this cell, or some previous cell, had enough padding to
+ * ensure sendme unpredictability. */
+ tor_assert_nonfatal(on_circuit->have_sent_sufficiently_random_cell);
+ /* Pick a new interval in which we need to send randomness. */
+ circuit_reset_sendme_randomness(on_circuit);
+ }
+
+ --on_circuit->send_randomness_after_n_cells;
+
+ return package_length;
}
/** If <b>conn</b> has an entire relay payload of bytes on its inbuf (or
@@ -2123,10 +2182,13 @@ connection_edge_package_raw_inbuf(edge_connection_t *conn, int package_partial,
}
length = connection_edge_get_inbuf_bytes_to_package(bytes_to_process,
- package_partial);
+ package_partial, circ);
if (!length)
return 0;
+ /* If we reach this point, we will definitely be packaging bytes into
+ * a cell. */
+
stats_n_data_bytes_packaged += length;
stats_n_data_cells_packaged += 1;
diff --git a/src/core/or/relay.h b/src/core/or/relay.h
index 97d5d6d0f2..0fc308f7df 100644
--- a/src/core/or/relay.h
+++ b/src/core/or/relay.h
@@ -42,6 +42,7 @@ int connection_edge_package_raw_inbuf(edge_connection_t *conn,
int package_partial,
int *max_cells);
void connection_edge_consider_sending_sendme(edge_connection_t *conn);
+void circuit_reset_sendme_randomness(circuit_t *circ);
extern uint64_t stats_n_data_cells_packaged;
extern uint64_t stats_n_data_bytes_packaged;