소스 검색

Merge remote-tracking branch 'teor/reject-tap-v6'

Nick Mathewson 7 년 전
부모
커밋
bbaa7d09a0
18개의 변경된 파일325개의 추가작업 그리고 93개의 파일을 삭제
  1. 15 0
      changes/reject-tap
  2. 0 10
      doc/tor.1.txt
  3. 166 64
      src/or/circuitbuild.c
  4. 5 0
      src/or/circuitbuild.h
  5. 4 2
      src/or/circuitlist.c
  6. 1 1
      src/or/config.c
  7. 14 0
      src/or/dirserv.c
  8. 6 0
      src/or/networkstatus.c
  9. 26 2
      src/or/nodelist.c
  10. 2 2
      src/or/onion.c
  11. 0 3
      src/or/or.h
  12. 6 1
      src/or/rendclient.c
  13. 8 0
      src/or/rendservice.c
  14. 2 0
      src/or/rendservice.h
  15. 4 0
      src/or/router.c
  16. 48 3
      src/or/routerlist.c
  17. 3 0
      src/or/routerlist.h
  18. 15 5
      src/test/test_dir.c

+ 15 - 0
changes/reject-tap

@@ -0,0 +1,15 @@
+  o Major bug fixes (circuit building):
+    - Tor authorities, relays, and clients only use ntor, except for
+      rare cases in the hidden service protocol.
+    - Authorities, relays and clients specifically check that each
+      descriptor has an ntor key.
+    - Clients avoid downloading a descriptor if the relay version is
+      too old to support ntor.
+    - Client code never chooses nodes without ntor keys: they will not
+      be selected during circuit-building, or as guards, or as directory
+      mirrors, or as introduction or rendezvous points.
+    - Circuit-building code assumes that all hops can use ntor,
+      except for rare hidden service protocol cases.
+    - Hidden service client to intro point and service to rendezvous point
+      connections use the TAP key supplied by the protocol.
+      Fixes bug 19163; bugfix on 0.2.4.18-rc.

+ 0 - 10
doc/tor.1.txt

@@ -1452,16 +1452,6 @@ The following options are useful only for clients (that is, if
     "auto" (recommended) then it is on for all clients that do not set
     "auto" (recommended) then it is on for all clients that do not set
     FetchUselessDescriptors. (Default: auto)
     FetchUselessDescriptors. (Default: auto)
 
 
-[[UseNTorHandshake]] **UseNTorHandshake** **0**|**1**|**auto**::
-    The "ntor" circuit-creation handshake is faster and (we think) more
-    secure than the original ("TAP") circuit handshake, but starting to use
-    it too early might make your client stand out. If this option is 0, your
-    Tor client won't use the ntor handshake. If it's 1, your Tor client
-    will use the ntor handshake to extend circuits through servers that
-    support it. If this option is "auto", then your client
-    will use the ntor handshake once enough directory authorities recommend
-    it. (Default: 1)
-
 [[PathBiasCircThreshold]] **PathBiasCircThreshold** __NUM__ +
 [[PathBiasCircThreshold]] **PathBiasCircThreshold** __NUM__ +
 
 
 [[PathBiasNoticeRate]] **PathBiasNoticeRate** __NUM__ +
 [[PathBiasNoticeRate]] **PathBiasNoticeRate** __NUM__ +

+ 166 - 64
src/or/circuitbuild.c

@@ -58,7 +58,6 @@ static crypt_path_t *onion_next_hop_in_cpath(crypt_path_t *cpath);
 static int onion_extend_cpath(origin_circuit_t *circ);
 static int onion_extend_cpath(origin_circuit_t *circ);
 static int count_acceptable_nodes(smartlist_t *routers);
 static int count_acceptable_nodes(smartlist_t *routers);
 static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice);
 static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice);
-static int circuits_can_use_ntor(void);
 
 
 /** This function tries to get a channel to the specified endpoint,
 /** This function tries to get a channel to the specified endpoint,
  * and then calls command_setup_channel() to give it the right
  * and then calls command_setup_channel() to give it the right
@@ -365,7 +364,7 @@ circuit_rep_hist_note_result(origin_circuit_t *circ)
   } while (hop!=circ->cpath);
   } while (hop!=circ->cpath);
 }
 }
 
 
-/** Return 1 iff at least one node in circ's cpath supports ntor. */
+/** Return 1 iff every node in circ's cpath definitely supports ntor. */
 static int
 static int
 circuit_cpath_supports_ntor(const origin_circuit_t *circ)
 circuit_cpath_supports_ntor(const origin_circuit_t *circ)
 {
 {
@@ -373,16 +372,19 @@ circuit_cpath_supports_ntor(const origin_circuit_t *circ)
 
 
   cpath = head = circ->cpath;
   cpath = head = circ->cpath;
   do {
   do {
-    if (cpath->extend_info &&
-        !tor_mem_is_zero(
-            (const char*)cpath->extend_info->curve25519_onion_key.public_key,
-            CURVE25519_PUBKEY_LEN))
-      return 1;
+    /* if the extend_info is missing, we can't tell if it supports ntor */
+    if (!cpath->extend_info) {
+      return 0;
+    }
 
 
+    /* if the key is blank, it definitely doesn't support ntor */
+    if (!extend_info_supports_ntor(cpath->extend_info)) {
+      return 0;
+    }
     cpath = cpath->next;
     cpath = cpath->next;
   } while (cpath != head);
   } while (cpath != head);
 
 
-  return 0;
+  return 1;
 }
 }
 
 
 /** Pick all the entries in our cpath. Stop and return 0 when we're
 /** Pick all the entries in our cpath. Stop and return 0 when we're
@@ -390,41 +392,61 @@ circuit_cpath_supports_ntor(const origin_circuit_t *circ)
 static int
 static int
 onion_populate_cpath(origin_circuit_t *circ)
 onion_populate_cpath(origin_circuit_t *circ)
 {
 {
-  int n_tries = 0;
-  const int using_ntor = circuits_can_use_ntor();
+  int r = 0;
 
 
-#define MAX_POPULATE_ATTEMPTS 32
+  /* onion_extend_cpath assumes these are non-NULL */
+  tor_assert(circ);
+  tor_assert(circ->build_state);
 
 
-  while (1) {
-    int r = onion_extend_cpath(circ);
+  while (r == 0) {
+    r = onion_extend_cpath(circ);
     if (r < 0) {
     if (r < 0) {
       log_info(LD_CIRC,"Generating cpath hop failed.");
       log_info(LD_CIRC,"Generating cpath hop failed.");
       return -1;
       return -1;
     }
     }
-    if (r == 1) {
-      /* This circuit doesn't need/shouldn't be forced to have an ntor hop */
-      if (circ->build_state->desired_path_len <= 1 || ! using_ntor)
-        return 0;
+  }
 
 
-      /* This circuit has an ntor hop. great! */
-      if (circuit_cpath_supports_ntor(circ))
-        return 0;
+  /* The path is complete */
+  tor_assert(r == 1);
 
 
-      /* No node in the circuit supports ntor.  Have we already tried too many
-       * times? */
-      if (++n_tries >= MAX_POPULATE_ATTEMPTS)
-        break;
+  /* Does every node in this path support ntor? */
+  int path_supports_ntor = circuit_cpath_supports_ntor(circ);
+
+  /* We would like every path to support ntor, but we have to allow for some
+   * edge cases. */
+  tor_assert(circuit_get_cpath_len(circ));
+  if (circuit_can_use_tap(circ)) {
+    /* Circuits from clients to intro points, and hidden services to
+     * rend points do not support ntor, because the hidden service protocol
+     * does not include ntor onion keys. This is also true for Tor2web clients
+     * and Single Onion Services. */
+    return 0;
+  }
 
 
-      /* Clear the path and retry */
-      circuit_clear_cpath(circ);
+  if (circuit_get_cpath_len(circ) == 1) {
+    /* Allow for bootstrapping: when we're fetching directly from a fallback,
+     * authority, or bridge, we have no way of knowing its ntor onion key
+     * before we connect to it. So instead, we try connecting, and end up using
+     * CREATE_FAST. */
+    tor_assert(circ->cpath);
+    tor_assert(circ->cpath->extend_info);
+    const node_t *node = node_get_by_id(
+                                    circ->cpath->extend_info->identity_digest);
+    /* If we don't know the node and its descriptor, we must be bootstrapping.
+     */
+    if (!node || !node_has_descriptor(node)) {
+      return 0;
     }
     }
   }
   }
-  log_warn(LD_CIRC, "I tried for %d times, but I couldn't build a %d-hop "
-           "circuit with at least one node that supports ntor.",
-           MAX_POPULATE_ATTEMPTS,
-           circ->build_state->desired_path_len);
 
 
-  return -1;
+  if (BUG(!path_supports_ntor)) {
+    /* If we're building a multi-hop path, and it's not one of the HS or
+     * bootstrapping exceptions, and it doesn't support ntor, something has
+     * gone wrong. */
+    return -1;
+  }
+
+  return 0;
 }
 }
 
 
 /** Create and return a new origin circuit. Initialize its purpose and
 /** Create and return a new origin circuit. Initialize its purpose and
@@ -757,10 +779,13 @@ should_use_create_fast_for_circuit(origin_circuit_t *circ)
   tor_assert(circ->cpath);
   tor_assert(circ->cpath);
   tor_assert(circ->cpath->extend_info);
   tor_assert(circ->cpath->extend_info);
 
 
-  if (!circ->cpath->extend_info->onion_key)
-    return 1; /* our hand is forced: only a create_fast will work. */
+  if (!circuit_has_usable_onion_key(circ)) {
+    /* We don't have ntor, and we don't have or can't use TAP,
+     * so our hand is forced: only a create_fast will work. */
+    return 1;
+  }
   if (public_server_mode(options)) {
   if (public_server_mode(options)) {
-    /* We're a server, and we know an onion key. We can choose.
+    /* We're a server, and we have a usable onion key. We can choose.
      * Prefer to blend our circuit into the other circuits we are
      * Prefer to blend our circuit into the other circuits we are
      * creating on behalf of others. */
      * creating on behalf of others. */
     return 0;
     return 0;
@@ -785,30 +810,20 @@ circuit_timeout_want_to_count_circ(origin_circuit_t *circ)
           && circ->build_state->desired_path_len == DEFAULT_ROUTE_LEN;
           && circ->build_state->desired_path_len == DEFAULT_ROUTE_LEN;
 }
 }
 
 
-/** Return true if the ntor handshake is enabled in the configuration, or if
- * it's been set to "auto" in the configuration and it's enabled in the
- * consensus. */
-static int
-circuits_can_use_ntor(void)
-{
-  const or_options_t *options = get_options();
-  if (options->UseNTorHandshake != -1)
-    return options->UseNTorHandshake;
-  return networkstatus_get_param(NULL, "UseNTorHandshake", 0, 0, 1);
-}
-
 /** Decide whether to use a TAP or ntor handshake for connecting to <b>ei</b>
 /** Decide whether to use a TAP or ntor handshake for connecting to <b>ei</b>
  * directly, and set *<b>cell_type_out</b> and *<b>handshake_type_out</b>
  * directly, and set *<b>cell_type_out</b> and *<b>handshake_type_out</b>
- * accordingly. */
+ * accordingly.
+ * Note that TAP handshakes are only used for direct connections:
+ *  - from Tor2web to intro points not in the client's consensus, and
+ *  - from Single Onions to rend points not in the service's consensus.
+ * This is checked in onion_populate_cpath. */
 static void
 static void
 circuit_pick_create_handshake(uint8_t *cell_type_out,
 circuit_pick_create_handshake(uint8_t *cell_type_out,
                               uint16_t *handshake_type_out,
                               uint16_t *handshake_type_out,
                               const extend_info_t *ei)
                               const extend_info_t *ei)
 {
 {
-  /* XXXX029 Remove support for deciding to use TAP. */
-  if (!tor_mem_is_zero((const char*)ei->curve25519_onion_key.public_key,
-                       CURVE25519_PUBKEY_LEN) &&
-      circuits_can_use_ntor()) {
+  /* XXXX030 Remove support for deciding to use TAP. */
+  if (extend_info_supports_ntor(ei)) {
     *cell_type_out = CELL_CREATE2;
     *cell_type_out = CELL_CREATE2;
     *handshake_type_out = ONION_HANDSHAKE_TYPE_NTOR;
     *handshake_type_out = ONION_HANDSHAKE_TYPE_NTOR;
     return;
     return;
@@ -822,7 +837,11 @@ circuit_pick_create_handshake(uint8_t *cell_type_out,
  * directly, and set *<b>handshake_type_out</b> accordingly. Decide whether,
  * directly, and set *<b>handshake_type_out</b> accordingly. Decide whether,
  * in extending through <b>node</b> to do so, we should use an EXTEND2 or an
  * in extending through <b>node</b> to do so, we should use an EXTEND2 or an
  * EXTEND cell to do so, and set *<b>cell_type_out</b> and
  * EXTEND cell to do so, and set *<b>cell_type_out</b> and
- * *<b>create_cell_type_out</b> accordingly. */
+ * *<b>create_cell_type_out</b> accordingly.
+ * Note that TAP handshakes are only used for extend handshakes:
+ *  - from clients to intro points, and
+ *  - from hidden services to rend points.
+ * This is checked in onion_populate_cpath. */
 static void
 static void
 circuit_pick_extend_handshake(uint8_t *cell_type_out,
 circuit_pick_extend_handshake(uint8_t *cell_type_out,
                               uint8_t *create_cell_type_out,
                               uint8_t *create_cell_type_out,
@@ -833,17 +852,27 @@ circuit_pick_extend_handshake(uint8_t *cell_type_out,
   uint8_t t;
   uint8_t t;
   circuit_pick_create_handshake(&t, handshake_type_out, ei);
   circuit_pick_create_handshake(&t, handshake_type_out, ei);
 
 
-  /* XXXX029 Remove support for deciding to use TAP. */
+  /* XXXX030 Remove support for deciding to use TAP. */
+
+  /* It is an error to extend if there is no previous node. */
+  tor_assert_nonfatal(node_prev);
+  /* It is an error for a node with a known version to be so old it does not
+   * support ntor. */
+  tor_assert_nonfatal(routerstatus_version_supports_ntor(node_prev->rs, 1));
+
+  /* Assume relays without tor versions or routerstatuses support ntor.
+   * The authorities enforce ntor support, and assuming and failing is better
+   * than allowing a malicious node to perform a protocol downgrade to TAP. */
   if (node_prev &&
   if (node_prev &&
       *handshake_type_out != ONION_HANDSHAKE_TYPE_TAP &&
       *handshake_type_out != ONION_HANDSHAKE_TYPE_TAP &&
       (node_has_curve25519_onion_key(node_prev) ||
       (node_has_curve25519_onion_key(node_prev) ||
-       (node_prev->rs && node_prev->rs->version_supports_extend2_cells))) {
-    *cell_type_out = RELAY_COMMAND_EXTEND2;
-    *create_cell_type_out = CELL_CREATE2;
-  } else {
-    *cell_type_out = RELAY_COMMAND_EXTEND;
-    *create_cell_type_out = CELL_CREATE;
-  }
+       (routerstatus_version_supports_ntor(node_prev->rs, 1)))) {
+        *cell_type_out = RELAY_COMMAND_EXTEND2;
+        *create_cell_type_out = CELL_CREATE2;
+      } else {
+        *cell_type_out = RELAY_COMMAND_EXTEND;
+        *create_cell_type_out = CELL_CREATE;
+      }
 }
 }
 
 
 /** This is the backbone function for building circuits.
 /** This is the backbone function for building circuits.
@@ -2058,15 +2087,18 @@ count_acceptable_nodes(smartlist_t *nodes)
     if (! node->is_running)
     if (! node->is_running)
 //      log_debug(LD_CIRC,"Nope, the directory says %d is not running.",i);
 //      log_debug(LD_CIRC,"Nope, the directory says %d is not running.",i);
       continue;
       continue;
+    /* XXX This clause makes us count incorrectly: if AllowInvalidRouters
+     * allows this node in some places, then we're getting an inaccurate
+     * count. For now, be conservative and don't count it. But later we
+     * should try to be smarter. */
     if (! node->is_valid)
     if (! node->is_valid)
 //      log_debug(LD_CIRC,"Nope, the directory says %d is not valid.",i);
 //      log_debug(LD_CIRC,"Nope, the directory says %d is not valid.",i);
       continue;
       continue;
     if (! node_has_descriptor(node))
     if (! node_has_descriptor(node))
       continue;
       continue;
-      /* XXX This clause makes us count incorrectly: if AllowInvalidRouters
-       * allows this node in some places, then we're getting an inaccurate
-       * count. For now, be conservative and don't count it. But later we
-       * should try to be smarter. */
+    /* The node has a descriptor, so we can just check the ntor key directly */
+    if (!node_has_curve25519_onion_key(node))
+      continue;
     ++num;
     ++num;
   } SMARTLIST_FOREACH_END(node);
   } SMARTLIST_FOREACH_END(node);
 
 
@@ -2356,6 +2388,14 @@ extend_info_from_node(const node_t *node, int for_direct_connect)
     log_warn(LD_CIRC, "Could not choose valid address for %s",
     log_warn(LD_CIRC, "Could not choose valid address for %s",
               node->ri ? node->ri->nickname : node->rs->nickname);
               node->ri ? node->ri->nickname : node->rs->nickname);
 
 
+  /* Every node we connect or extend to must support ntor */
+  if (!node_has_curve25519_onion_key(node)) {
+    log_fn(LOG_PROTOCOL_WARN, LD_CIRC,
+           "Attempted to create extend_info for a node that does not support "
+           "ntor: %s", node_describe(node));
+    return NULL;
+  }
+
   if (valid_addr && node->ri)
   if (valid_addr && node->ri)
     return extend_info_new(node->ri->nickname,
     return extend_info_new(node->ri->nickname,
                              node->identity,
                              node->identity,
@@ -2441,3 +2481,65 @@ extend_info_addr_is_allowed(const tor_addr_t *addr)
   return 0;
   return 0;
 }
 }
 
 
+/* Does ei have a valid TAP key? */
+int
+extend_info_supports_tap(const extend_info_t* ei)
+{
+  tor_assert(ei);
+  /* Valid TAP keys are not NULL */
+  return ei->onion_key != NULL;
+}
+
+/* Does ei have a valid ntor key? */
+int
+extend_info_supports_ntor(const extend_info_t* ei)
+{
+  tor_assert(ei);
+  /* Valid ntor keys have at least one non-zero byte */
+  return !tor_mem_is_zero(
+                          (const char*)ei->curve25519_onion_key.public_key,
+                          CURVE25519_PUBKEY_LEN);
+}
+
+/* Is circuit purpose allowed to use the deprecated TAP encryption protocol?
+ * The hidden service protocol still uses TAP for some connections, because
+ * ntor onion keys aren't included in HS descriptors or INTRODUCE cells. */
+static int
+circuit_purpose_can_use_tap_impl(uint8_t purpose)
+{
+  return (purpose == CIRCUIT_PURPOSE_S_CONNECT_REND ||
+          purpose == CIRCUIT_PURPOSE_C_INTRODUCING);
+}
+
+/* Is circ allowed to use the deprecated TAP encryption protocol?
+ * The hidden service protocol still uses TAP for some connections, because
+ * ntor onion keys aren't included in HS descriptors or INTRODUCE cells. */
+int
+circuit_can_use_tap(const origin_circuit_t *circ)
+{
+  tor_assert(circ);
+  tor_assert(circ->cpath);
+  tor_assert(circ->cpath->extend_info);
+  return (circuit_purpose_can_use_tap_impl(circ->base_.purpose) &&
+          extend_info_supports_tap(circ->cpath->extend_info));
+}
+
+/* Does circ have an onion key which it's allowed to use? */
+int
+circuit_has_usable_onion_key(const origin_circuit_t *circ)
+{
+  tor_assert(circ);
+  tor_assert(circ->cpath);
+  tor_assert(circ->cpath->extend_info);
+  return (extend_info_supports_ntor(circ->cpath->extend_info) ||
+          circuit_can_use_tap(circ));
+}
+
+/* Does ei have an onion key which it would prefer to use?
+ * Currently, we prefer ntor keys*/
+int
+extend_info_has_preferred_onion_key(const extend_info_t* ei)
+{
+  tor_assert(ei);
+  return extend_info_supports_ntor(ei);
+}

+ 5 - 0
src/or/circuitbuild.h

@@ -54,6 +54,11 @@ extend_info_t *extend_info_from_node(const node_t *r, int for_direct_connect);
 extend_info_t *extend_info_dup(extend_info_t *info);
 extend_info_t *extend_info_dup(extend_info_t *info);
 void extend_info_free(extend_info_t *info);
 void extend_info_free(extend_info_t *info);
 int extend_info_addr_is_allowed(const tor_addr_t *addr);
 int extend_info_addr_is_allowed(const tor_addr_t *addr);
+int extend_info_supports_tap(const extend_info_t* ei);
+int extend_info_supports_ntor(const extend_info_t* ei);
+int circuit_can_use_tap(const origin_circuit_t *circ);
+int circuit_has_usable_onion_key(const origin_circuit_t *circ);
+int extend_info_has_preferred_onion_key(const extend_info_t* ei);
 const node_t *build_state_get_exit_node(cpath_build_state_t *state);
 const node_t *build_state_get_exit_node(cpath_build_state_t *state);
 const char *build_state_get_exit_nickname(cpath_build_state_t *state);
 const char *build_state_get_exit_nickname(cpath_build_state_t *state);
 
 

+ 4 - 2
src/or/circuitlist.c

@@ -1613,7 +1613,8 @@ circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info,
   return best;
   return best;
 }
 }
 
 
-/** Return the number of hops in circuit's path. */
+/** Return the number of hops in circuit's path. If circ has no entries,
+ * or is NULL, returns 0. */
 int
 int
 circuit_get_cpath_len(origin_circuit_t *circ)
 circuit_get_cpath_len(origin_circuit_t *circ)
 {
 {
@@ -1629,7 +1630,8 @@ circuit_get_cpath_len(origin_circuit_t *circ)
 }
 }
 
 
 /** Return the <b>hopnum</b>th hop in <b>circ</b>->cpath, or NULL if there
 /** Return the <b>hopnum</b>th hop in <b>circ</b>->cpath, or NULL if there
- * aren't that many hops in the list. */
+ * aren't that many hops in the list. <b>hopnum</b> starts at 1.
+ * Returns NULL if <b>hopnum</b> is 0 or negative. */
 crypt_path_t *
 crypt_path_t *
 circuit_get_cpath_hop(origin_circuit_t *circ, int hopnum)
 circuit_get_cpath_hop(origin_circuit_t *circ, int hopnum)
 {
 {

+ 1 - 1
src/or/config.c

@@ -438,7 +438,7 @@ static config_var_t option_vars_[] = {
   V(UseEntryGuardsAsDirGuards,   BOOL,     "1"),
   V(UseEntryGuardsAsDirGuards,   BOOL,     "1"),
   V(UseGuardFraction,            AUTOBOOL, "auto"),
   V(UseGuardFraction,            AUTOBOOL, "auto"),
   V(UseMicrodescriptors,         AUTOBOOL, "auto"),
   V(UseMicrodescriptors,         AUTOBOOL, "auto"),
-  V(UseNTorHandshake,            AUTOBOOL, "1"),
+  OBSOLETE("UseNTorHandshake"),
   V(User,                        STRING,   NULL),
   V(User,                        STRING,   NULL),
   OBSOLETE("UserspaceIOCPBuffers"),
   OBSOLETE("UserspaceIOCPBuffers"),
   V(AuthDirSharedRandomness,     BOOL,     "1"),
   V(AuthDirSharedRandomness,     BOOL,     "1"),

+ 14 - 0
src/or/dirserv.c

@@ -255,6 +255,20 @@ dirserv_router_get_status(const routerinfo_t *router, const char **msg,
     return FP_REJECT;
     return FP_REJECT;
   }
   }
 
 
+  /* dirserv_get_status_impl already rejects versions older than 0.2.4.18-rc,
+   * and onion_curve25519_pkey was introduced in 0.2.4.8-alpha.
+   * But just in case a relay doesn't provide or lies about its version, or
+   * doesn't include an ntor key in its descriptor, check that it exists,
+   * and is non-zero (clients check that it's non-zero before using it). */
+  if (!routerinfo_has_curve25519_onion_key(router)) {
+    log_fn(severity, LD_DIR,
+           "Descriptor from router %s is missing an ntor curve25519 onion "
+           "key.", router_describe(router));
+    if (msg)
+      *msg = "Missing ntor curve25519 onion key. Please upgrade!";
+    return FP_REJECT;
+  }
+
   if (router->cache_info.signing_key_cert) {
   if (router->cache_info.signing_key_cert) {
     /* This has an ed25519 identity key. */
     /* This has an ed25519 identity key. */
     if (KEYPIN_MISMATCH ==
     if (KEYPIN_MISMATCH ==

+ 6 - 0
src/or/networkstatus.c

@@ -2275,6 +2275,12 @@ client_would_use_router(const routerstatus_t *rs, time_t now,
     /* We'd drop it immediately for being too old. */
     /* We'd drop it immediately for being too old. */
     return 0;
     return 0;
   }
   }
+  if (!routerstatus_version_supports_ntor(rs, 1)) {
+    /* We'd ignore it because it doesn't support ntor.
+     * If we don't know the version, download the descriptor so we can
+     * check if it supports ntor. */
+    return 0;
+  }
   return 1;
   return 1;
 }
 }
 
 

+ 26 - 2
src/or/nodelist.c

@@ -1173,14 +1173,38 @@ node_get_pref_ipv6_dirport(const node_t *node, tor_addr_port_t *ap_out)
   }
   }
 }
 }
 
 
+/** Return true iff <b>md</b> has a curve25519 onion key.
+ * Use node_has_curve25519_onion_key() instead of calling this directly. */
+static int
+microdesc_has_curve25519_onion_key(const microdesc_t *md)
+{
+  if (!md) {
+    return 0;
+  }
+
+  if (!md->onion_curve25519_pkey) {
+    return 0;
+  }
+
+  if (tor_mem_is_zero((const char*)md->onion_curve25519_pkey->public_key,
+                      CURVE25519_PUBKEY_LEN)) {
+    return 0;
+  }
+
+  return 1;
+}
+
 /** Return true iff <b>node</b> has a curve25519 onion key. */
 /** Return true iff <b>node</b> has a curve25519 onion key. */
 int
 int
 node_has_curve25519_onion_key(const node_t *node)
 node_has_curve25519_onion_key(const node_t *node)
 {
 {
+  if (!node)
+    return 0;
+
   if (node->ri)
   if (node->ri)
-    return node->ri->onion_curve25519_pkey != NULL;
+    return routerinfo_has_curve25519_onion_key(node->ri);
   else if (node->md)
   else if (node->md)
-    return node->md->onion_curve25519_pkey != NULL;
+    return microdesc_has_curve25519_onion_key(node->md);
   else
   else
     return 0;
     return 0;
 }
 }

+ 2 - 2
src/or/onion.c

@@ -11,6 +11,7 @@
  **/
  **/
 
 
 #include "or.h"
 #include "or.h"
+#include "circuitbuild.h"
 #include "circuitlist.h"
 #include "circuitlist.h"
 #include "config.h"
 #include "config.h"
 #include "cpuworker.h"
 #include "cpuworker.h"
@@ -438,8 +439,7 @@ onion_skin_create(int type,
     r = CREATE_FAST_LEN;
     r = CREATE_FAST_LEN;
     break;
     break;
   case ONION_HANDSHAKE_TYPE_NTOR:
   case ONION_HANDSHAKE_TYPE_NTOR:
-    if (tor_mem_is_zero((const char*)node->curve25519_onion_key.public_key,
-                        CURVE25519_PUBKEY_LEN))
+    if (!extend_info_supports_ntor(node))
       return -1;
       return -1;
     if (onion_skin_ntor_create((const uint8_t*)node->identity_digest,
     if (onion_skin_ntor_create((const uint8_t*)node->identity_digest,
                                &node->curve25519_onion_key,
                                &node->curve25519_onion_key,

+ 0 - 3
src/or/or.h

@@ -4384,9 +4384,6 @@ typedef struct {
 
 
   char *TLSECGroup; /**< One of "P256", "P224", or nil for auto */
   char *TLSECGroup; /**< One of "P256", "P224", or nil for auto */
 
 
-  /** Autobool: should we use the ntor handshake if we can? */
-  int UseNTorHandshake;
-
   /** Fraction: */
   /** Fraction: */
   double PathsNeededToBuildCircuits;
   double PathsNeededToBuildCircuits;
 
 

+ 6 - 1
src/or/rendclient.c

@@ -1368,8 +1368,13 @@ rend_client_get_random_intro_impl(const rend_cache_entry_t *entry,
 
 
   i = crypto_rand_int(smartlist_len(usable_nodes));
   i = crypto_rand_int(smartlist_len(usable_nodes));
   intro = smartlist_get(usable_nodes, i);
   intro = smartlist_get(usable_nodes, i);
+  if (BUG(!intro->extend_info)) {
+    /* This should never happen, but it isn't fatal, just try another */
+    smartlist_del(usable_nodes, i);
+    goto again;
+  }
   /* Do we need to look up the router or is the extend info complete? */
   /* Do we need to look up the router or is the extend info complete? */
-  if (!intro->extend_info->onion_key) {
+  if (!extend_info_supports_tap(intro->extend_info)) {
     const node_t *node;
     const node_t *node;
     extend_info_t *new_extend_info;
     extend_info_t *new_extend_info;
     if (tor_digest_is_zero(intro->extend_info->identity_digest))
     if (tor_digest_is_zero(intro->extend_info->identity_digest))

+ 8 - 0
src/or/rendservice.c

@@ -3896,3 +3896,11 @@ rend_service_set_connection_addr_port(edge_connection_t *conn,
     return -2;
     return -2;
 }
 }
 
 
+/* Stub that should be replaced with the #17178 version of the function
+ * when merging. */
+int
+rend_service_allow_direct_connection(const or_options_t *options)
+{
+  (void)options;
+  return 0;
+}

+ 2 - 0
src/or/rendservice.h

@@ -131,5 +131,7 @@ void directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
                               const char *service_id, int seconds_valid);
                               const char *service_id, int seconds_valid);
 void rend_service_desc_has_uploaded(const rend_data_t *rend_data);
 void rend_service_desc_has_uploaded(const rend_data_t *rend_data);
 
 
+int rend_service_allow_direct_connection(const or_options_t *options);
+
 #endif
 #endif
 
 

+ 4 - 0
src/or/router.c

@@ -2836,6 +2836,10 @@ router_dump_router_to_string(routerinfo_t *router,
                   (const char *)router->onion_curve25519_pkey->public_key,
                   (const char *)router->onion_curve25519_pkey->public_key,
                   CURVE25519_PUBKEY_LEN, BASE64_ENCODE_MULTILINE);
                   CURVE25519_PUBKEY_LEN, BASE64_ENCODE_MULTILINE);
     smartlist_add_asprintf(chunks, "ntor-onion-key %s", kbuf);
     smartlist_add_asprintf(chunks, "ntor-onion-key %s", kbuf);
+  } else {
+    /* Authorities will start rejecting relays without ntor keys in 0.2.9 */
+    log_err(LD_BUG, "A relay must have an ntor onion key");
+    goto err;
   }
   }
 
 
   /* Write the exit policy to the end of 's'. */
   /* Write the exit policy to the end of 's'. */

+ 48 - 3
src/or/routerlist.c

@@ -2260,10 +2260,16 @@ router_add_running_nodes_to_smartlist(smartlist_t *sl, int allow_invalid,
       continue;
       continue;
     if (node_is_unreliable(node, need_uptime, need_capacity, need_guard))
     if (node_is_unreliable(node, need_uptime, need_capacity, need_guard))
       continue;
       continue;
-    /* Choose a node with an OR address that matches the firewall rules,
-     * if we are making a direct connection */
+    /* Don't choose nodes if we are certain they can't do ntor */
+    if (node->rs && !routerstatus_version_supports_ntor(node->rs, 1))
+      continue;
+    if ((node->ri || node->md) && !node_has_curve25519_onion_key(node))
+      continue;
+    /* Choose a node with an OR address that matches the firewall rules */
     if (direct_conn && check_reach &&
     if (direct_conn && check_reach &&
-        !fascist_firewall_allows_node(node, FIREWALL_OR_CONNECTION, pref_addr))
+        !fascist_firewall_allows_node(node,
+                                      FIREWALL_OR_CONNECTION,
+                                      pref_addr))
       continue;
       continue;
 
 
     smartlist_add(sl, (void *)node);
     smartlist_add(sl, (void *)node);
@@ -5497,6 +5503,45 @@ routerinfo_incompatible_with_extrainfo(const crypto_pk_t *identity_pkey,
   return r;
   return r;
 }
 }
 
 
+/* Does ri have a valid ntor onion key?
+ * Valid ntor onion keys exist and have at least one non-zero byte. */
+int
+routerinfo_has_curve25519_onion_key(const routerinfo_t *ri)
+{
+  if (!ri) {
+    return 0;
+  }
+
+  if (!ri->onion_curve25519_pkey) {
+    return 0;
+  }
+
+  if (tor_mem_is_zero((const char*)ri->onion_curve25519_pkey->public_key,
+                      CURVE25519_PUBKEY_LEN)) {
+    return 0;
+  }
+
+  return 1;
+}
+
+/* Is rs running a tor version known to support ntor?
+ * If allow_unknown_versions is true, return true if the version is unknown.
+ * Otherwise, return false if the version is unknown. */
+int
+routerstatus_version_supports_ntor(const routerstatus_t *rs,
+                                   int allow_unknown_versions)
+{
+  if (!rs) {
+    return allow_unknown_versions;
+  }
+
+  if (!rs->version_known) {
+    return allow_unknown_versions;
+  }
+
+  return rs->version_supports_extend2_cells;
+}
+
 /** Assert that the internal representation of <b>rl</b> is
 /** Assert that the internal representation of <b>rl</b> is
  * self-consistent. */
  * self-consistent. */
 void
 void

+ 3 - 0
src/or/routerlist.h

@@ -206,6 +206,9 @@ int routerinfo_incompatible_with_extrainfo(const crypto_pk_t *ri,
                                            extrainfo_t *ei,
                                            extrainfo_t *ei,
                                            signed_descriptor_t *sd,
                                            signed_descriptor_t *sd,
                                            const char **msg);
                                            const char **msg);
+int routerinfo_has_curve25519_onion_key(const routerinfo_t *ri);
+int routerstatus_version_supports_ntor(const routerstatus_t *rs,
+                                       int allow_unknown_versions);
 
 
 void routerlist_assert_ok(const routerlist_t *rl);
 void routerlist_assert_ok(const routerlist_t *rl);
 const char *esc_router_info(const routerinfo_t *router);
 const char *esc_router_info(const routerinfo_t *router);

+ 15 - 5
src/test/test_dir.c

@@ -116,6 +116,7 @@ test_dir_formats(void *arg)
   const addr_policy_t *p;
   const addr_policy_t *p;
   time_t now = time(NULL);
   time_t now = time(NULL);
   port_cfg_t orport, dirport;
   port_cfg_t orport, dirport;
+  char cert_buf[256];
 
 
   (void)arg;
   (void)arg;
   pk1 = pk_generate(0);
   pk1 = pk_generate(0);
@@ -135,6 +136,11 @@ test_dir_formats(void *arg)
   tor_addr_parse(&r1->ipv6_addr, "1:2:3:4::");
   tor_addr_parse(&r1->ipv6_addr, "1:2:3:4::");
   r1->ipv6_orport = 9999;
   r1->ipv6_orport = 9999;
   r1->onion_pkey = crypto_pk_dup_key(pk1);
   r1->onion_pkey = crypto_pk_dup_key(pk1);
+  /* Fake just enough of an ntor key to get by */
+  curve25519_keypair_t r1_onion_keypair;
+  curve25519_keypair_generate(&r1_onion_keypair, 0);
+  r1->onion_curve25519_pkey = tor_memdup(&r1_onion_keypair.pubkey,
+                                         sizeof(curve25519_public_key_t));
   r1->identity_pkey = crypto_pk_dup_key(pk2);
   r1->identity_pkey = crypto_pk_dup_key(pk2);
   r1->bandwidthrate = 1000;
   r1->bandwidthrate = 1000;
   r1->bandwidthburst = 5000;
   r1->bandwidthburst = 5000;
@@ -167,11 +173,6 @@ test_dir_formats(void *arg)
                                          &kp2.pubkey,
                                          &kp2.pubkey,
                                          now, 86400,
                                          now, 86400,
                                          CERT_FLAG_INCLUDE_SIGNING_KEY);
                                          CERT_FLAG_INCLUDE_SIGNING_KEY);
-  char cert_buf[256];
-  base64_encode(cert_buf, sizeof(cert_buf),
-                (const char*)r2->cache_info.signing_key_cert->encoded,
-                r2->cache_info.signing_key_cert->encoded_len,
-                BASE64_ENCODE_MULTILINE);
   r2->platform = tor_strdup(platform);
   r2->platform = tor_strdup(platform);
   r2->cache_info.published_on = 5;
   r2->cache_info.published_on = 5;
   r2->or_port = 9005;
   r2->or_port = 9005;
@@ -247,6 +248,11 @@ test_dir_formats(void *arg)
   strlcat(buf2, "hidden-service-dir\n", sizeof(buf2));
   strlcat(buf2, "hidden-service-dir\n", sizeof(buf2));
   strlcat(buf2, "contact Magri White <magri@elsewhere.example.com>\n",
   strlcat(buf2, "contact Magri White <magri@elsewhere.example.com>\n",
           sizeof(buf2));
           sizeof(buf2));
+  strlcat(buf2, "ntor-onion-key ", sizeof(buf2));
+  base64_encode(cert_buf, sizeof(cert_buf),
+                (const char*)r1_onion_keypair.pubkey.public_key, 32,
+                BASE64_ENCODE_MULTILINE);
+  strlcat(buf2, cert_buf, sizeof(buf2));
   strlcat(buf2, "reject *:*\n", sizeof(buf2));
   strlcat(buf2, "reject *:*\n", sizeof(buf2));
   strlcat(buf2, "tunnelled-dir-server\nrouter-signature\n", sizeof(buf2));
   strlcat(buf2, "tunnelled-dir-server\nrouter-signature\n", sizeof(buf2));
   buf[strlen(buf2)] = '\0'; /* Don't compare the sig; it's never the same
   buf[strlen(buf2)] = '\0'; /* Don't compare the sig; it's never the same
@@ -276,6 +282,10 @@ test_dir_formats(void *arg)
           "router Fred 10.3.2.1 9005 0 0\n"
           "router Fred 10.3.2.1 9005 0 0\n"
           "identity-ed25519\n"
           "identity-ed25519\n"
           "-----BEGIN ED25519 CERT-----\n", sizeof(buf2));
           "-----BEGIN ED25519 CERT-----\n", sizeof(buf2));
+  base64_encode(cert_buf, sizeof(cert_buf),
+                (const char*)r2->cache_info.signing_key_cert->encoded,
+                r2->cache_info.signing_key_cert->encoded_len,
+                BASE64_ENCODE_MULTILINE);
   strlcat(buf2, cert_buf, sizeof(buf2));
   strlcat(buf2, cert_buf, sizeof(buf2));
   strlcat(buf2, "-----END ED25519 CERT-----\n", sizeof(buf2));
   strlcat(buf2, "-----END ED25519 CERT-----\n", sizeof(buf2));
   strlcat(buf2, "master-key-ed25519 ", sizeof(buf2));
   strlcat(buf2, "master-key-ed25519 ", sizeof(buf2));