mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-27 22:03:31 +01:00
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.
This commit is contained in:
parent
530d1179ff
commit
0bc1241494
6
changes/ticket26846
Normal file
6
changes/ticket26846
Normal file
@ -0,0 +1,6 @@
|
||||
o Minor features (authenticated SENDME):
|
||||
- Ensure that there is enough randomness on every circuit
|
||||
to prevent an attacker from successfully predicting what SENDME cells
|
||||
they will need to send: at a random interval, if we have not send
|
||||
randomness already, leave some extra space at the end of a cell that
|
||||
we can fill with random bytes. Closes ticket 26846.
|
@ -120,7 +120,7 @@ problem function-size /src/core/or/connection_or.c:connection_or_compute_authent
|
||||
problem file-size /src/core/or/policies.c 3249
|
||||
problem function-size /src/core/or/policies.c:policy_summarize() 107
|
||||
problem function-size /src/core/or/protover.c:protover_all_supported() 117
|
||||
problem file-size /src/core/or/relay.c 3173
|
||||
problem file-size /src/core/or/relay.c 3250
|
||||
problem function-size /src/core/or/relay.c:circuit_receive_relay_cell() 127
|
||||
problem function-size /src/core/or/relay.c:relay_send_command_from_edge_() 116
|
||||
problem function-size /src/core/or/relay.c:connection_ap_process_end_not_open() 194
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
@ -2027,27 +2031,82 @@ uint64_t stats_n_data_cells_received = 0;
|
||||
* ever received were completely full of data. */
|
||||
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;
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user