]> git.ipfire.org Git - thirdparty/tor.git/commitdiff
Make sure that we send at least some random data in RELAY_DATA cells
authorNick Mathewson <nickm@torproject.org>
Fri, 17 May 2019 15:03:16 +0000 (11:03 -0400)
committerGeorge Kadianakis <desnacked@riseup.net>
Mon, 27 May 2019 11:20:07 +0000 (14:20 +0300)
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.

changes/ticket26846 [new file with mode: 0644]
scripts/maint/practracker/exceptions.txt
src/core/or/circuit_st.h
src/core/or/circuitlist.c
src/core/or/circuitlist.h
src/core/or/relay.c
src/core/or/relay.h

diff --git a/changes/ticket26846 b/changes/ticket26846
new file mode 100644 (file)
index 0000000..a18e231
--- /dev/null
@@ -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.
index 712e59dc5bbc6eacdc27048afe9d6ee37c2a23ec..3a32e97bebbdf5c6d17f8c15e3dcbfbf4b334908 100644 (file)
@@ -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
index 499bf93d6bbc923878598a485de32bdce56311ea..3c7b931614b1e081b5c9b531733eed2a5e30818d 100644 (file)
@@ -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.
index 72952a8a522a1f406e5890f5c51f3f87a7a69da8..ebbe7f0824acd44564c5c83b7c18fe9f274ab381 100644 (file)
@@ -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);
index 6f5fce48750a21e07b7b97591b45230af9494c21..80c1f7ac4e6a7d56e7be5ca0336ca138489f6212 100644 (file)
@@ -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);
index 7a121780af34b3c9d77aff049e807a349a06fa2b..c48147dff8a74c36bfd0c724a2581fe69080840e 100644 (file)
@@ -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;
 
index 97d5d6d0f274b734d45c6173d6d3323fc86888f4..0fc308f7df982b0243d38b7ffaf35c4c27a368cb 100644 (file)
@@ -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;