diff --git a/changes/ticket20895 b/changes/ticket20895
new file mode 100644
index 0000000000..a1d8204997
--- /dev/null
+++ b/changes/ticket20895
@@ -0,0 +1,6 @@
+ o Minor features (forward-compatibility):
+ - If a relay supports some link authentication protocol that we do not
+ recognize, then include that relay's ed25519 key when telling other
+ relays to extend to it. Previously, we treated future versions as if
+ they were too old to support ed25519 link authentication.
+ Closes ticket 20895.
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index 279308afcb..45bf019545 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -1290,7 +1290,7 @@ circuit_extend(cell_t *cell, circuit_t *circ)
const node_t *node = node_get_by_id((const char*)ec.node_id);
const ed25519_public_key_t *node_ed_id = NULL;
if (node &&
- node_supports_ed25519_link_authentication(node) &&
+ node_supports_ed25519_link_authentication(node, 1) &&
(node_ed_id = node_get_ed25519_id(node))) {
ed25519_pubkey_copy(&ec.ed_pubkey, node_ed_id);
}
@@ -2698,7 +2698,7 @@ extend_info_from_node(const node_t *node, int for_direct_connect)
/* Don't send the ed25519 pubkey unless the target node actually supports
* authenticating with it. */
- if (node_supports_ed25519_link_authentication(node)) {
+ if (node_supports_ed25519_link_authentication(node, 0)) {
log_info(LD_CIRC, "Including Ed25519 ID for %s", node_describe(node));
ed_pubkey = node_get_ed25519_id(node);
} else if (node_get_ed25519_id(node)) {
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index d890b58da6..29c1166b3c 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -886,7 +886,7 @@ connection_or_check_canonicity(or_connection_t *conn, int started_here)
const node_t *r = node_get_by_id(id_digest);
if (r &&
- node_supports_ed25519_link_authentication(r) &&
+ node_supports_ed25519_link_authentication(r, 1) &&
! node_ed25519_id_matches(r, ed_id)) {
/* If this node is capable of proving an ed25519 ID,
* we can't call this a canonical connection unless both IDs match. */
diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index 06ac15d587..75cd0879aa 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -3284,7 +3284,7 @@ dirserv_orconn_tls_done(const tor_addr_t *addr,
ri = node->ri;
if (get_options()->AuthDirTestEd25519LinkKeys &&
- node_supports_ed25519_link_authentication(node) &&
+ node_supports_ed25519_link_authentication(node, 1) &&
ri->cache_info.signing_key_cert) {
/* We allow the node to have an ed25519 key if we haven't been told one in
* the routerinfo, but if we *HAVE* been told one in the routerinfo, it
@@ -3367,7 +3367,7 @@ dirserv_single_reachability_test(time_t now, routerinfo_t *router)
tor_assert(node);
if (options->AuthDirTestEd25519LinkKeys &&
- node_supports_ed25519_link_authentication(node)) {
+ node_supports_ed25519_link_authentication(node, 1)) {
ed_id_key = &router->cache_info.signing_key_cert->signing_key;
} else {
ed_id_key = NULL;
diff --git a/src/or/hs_service.c b/src/or/hs_service.c
index 5759aa8127..5ae7614e29 100644
--- a/src/or/hs_service.c
+++ b/src/or/hs_service.c
@@ -1494,7 +1494,7 @@ pick_intro_point(unsigned int direct_conn, smartlist_t *exclude_nodes)
/* Let's do a basic sanity check here so that we don't end up advertising the
* ed25519 identity key of relays that don't actually support the link
* protocol */
- if (!node_supports_ed25519_link_authentication(node)) {
+ if (!node_supports_ed25519_link_authentication(node, 0)) {
tor_assert_nonfatal(ed25519_public_key_is_zero(&info->ed_identity));
}
diff --git a/src/or/nodelist.c b/src/or/nodelist.c
index 00b8fb144f..1f2d37f285 100644
--- a/src/or/nodelist.c
+++ b/src/or/nodelist.c
@@ -962,23 +962,29 @@ node_ed25519_id_matches(const node_t *node, const ed25519_public_key_t *id)
}
/** Return true iff node supports authenticating itself
- * by ed25519 ID during the link handshake in a way that we can understand
- * when we probe it. */
+ * by ed25519 ID during the link handshake. If compatible_with_us,
+ * it needs to be using a link authentication method that we understand.
+ * If not, any plausible link authentication method will do. */
int
-node_supports_ed25519_link_authentication(const node_t *node)
+node_supports_ed25519_link_authentication(const node_t *node,
+ int compatible_with_us)
{
- /* XXXX Oh hm. What if some day in the future there are link handshake
- * versions that aren't 3 but which are ed25519 */
if (! node_get_ed25519_id(node))
return 0;
if (node->ri) {
const char *protos = node->ri->protocol_list;
if (protos == NULL)
return 0;
- return protocol_list_supports_protocol(protos, PRT_LINKAUTH, 3);
+ if (compatible_with_us)
+ return protocol_list_supports_protocol(protos, PRT_LINKAUTH, 3);
+ else
+ return protocol_list_supports_protocol_or_later(protos, PRT_LINKAUTH, 3);
}
if (node->rs) {
- return node->rs->supports_ed25519_link_handshake;
+ if (compatible_with_us)
+ return node->rs->supports_ed25519_link_handshake_compat;
+ else
+ return node->rs->supports_ed25519_link_handshake_any;
}
tor_assert_nonfatal_unreached_once();
return 0;
diff --git a/src/or/nodelist.h b/src/or/nodelist.h
index 427e449ad9..58743c5e78 100644
--- a/src/or/nodelist.h
+++ b/src/or/nodelist.h
@@ -65,7 +65,8 @@ const smartlist_t *node_get_declared_family(const node_t *node);
const ed25519_public_key_t *node_get_ed25519_id(const node_t *node);
int node_ed25519_id_matches(const node_t *node,
const ed25519_public_key_t *id);
-int node_supports_ed25519_link_authentication(const node_t *node);
+int node_supports_ed25519_link_authentication(const node_t *node,
+ int compatible_with_us);
int node_supports_v3_hsdir(const node_t *node);
int node_supports_ed25519_hs_intro(const node_t *node);
int node_supports_v3_rendezvous_point(const node_t *node);
diff --git a/src/or/or.h b/src/or/or.h
index 161d80ed96..8e1d15b8cb 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -2316,8 +2316,12 @@ typedef struct routerstatus_t {
unsigned int supports_extend2_cells:1;
/** True iff this router has a protocol list that allows it to negotiate
- * ed25519 identity keys on a link handshake. */
- unsigned int supports_ed25519_link_handshake:1;
+ * ed25519 identity keys on a link handshake with us. */
+ unsigned int supports_ed25519_link_handshake_compat:1;
+
+ /** True iff this router has a protocol list that allows it to negotiate
+ * ed25519 identity keys on a link handshake, at all. */
+ unsigned int supports_ed25519_link_handshake_any:1;
/** True iff this router has a protocol list that allows it to be an
* introduction point supporting ed25519 authentication key which is part of
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index 08c3fc0a02..b26231db01 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -2701,8 +2701,10 @@ routerstatus_parse_entry_from_string(memarea_t *area,
rs->protocols_known = 1;
rs->supports_extend2_cells =
protocol_list_supports_protocol(tok->args[0], PRT_RELAY, 2);
- rs->supports_ed25519_link_handshake =
+ rs->supports_ed25519_link_handshake_compat =
protocol_list_supports_protocol(tok->args[0], PRT_LINKAUTH, 3);
+ rs->supports_ed25519_link_handshake_any =
+ protocol_list_supports_protocol_or_later(tok->args[0], PRT_LINKAUTH, 3);
rs->supports_ed25519_hs_intro =
protocol_list_supports_protocol(tok->args[0], PRT_HSINTRO, 4);
rs->supports_v3_hsdir =