[tor-commits] [tor] 10/77: hs: Priority queue for rendezvous requests

gitolite role git at cupani.torproject.org
Wed May 10 15:46:54 UTC 2023


This is an automated email from the git hooks/post-receive script.

dgoulet pushed a commit to branch main
in repository tor.

commit 4eb783e97b20c830a1a4dff91cef13bcfd4f713f
Author: David Goulet <dgoulet at torproject.org>
AuthorDate: Wed Jun 29 11:56:05 2022 -0400

    hs: Priority queue for rendezvous requests
    
    If PoW are enabled, use a priority queue by effort for the rendezvous
    requests hooked into the mainloop.
    
    Signed-off-by: David Goulet <dgoulet at torproject.org>
---
 src/feature/hs/hs_cell.c               |  83 +++++++++++++++++--
 src/feature/hs/hs_cell.h               |   2 +
 src/feature/hs/hs_circuit.c            | 142 ++++++++++++++++++++++++++++++---
 src/feature/hs/hs_circuit.h            |  21 ++++-
 src/test/test_hs_service.c             |  10 ++-
 src/trunnel/hs/cell_introduce1.h       |   4 +-
 src/trunnel/hs/cell_introduce1.trunnel |   4 +-
 7 files changed, 236 insertions(+), 30 deletions(-)

diff --git a/src/feature/hs/hs_cell.c b/src/feature/hs/hs_cell.c
index 603d997c42..004a7fcbe1 100644
--- a/src/feature/hs/hs_cell.c
+++ b/src/feature/hs/hs_cell.c
@@ -391,7 +391,7 @@ build_introduce_pow_extension(const hs_pow_solution_t *pow_solution,
 
   /* We are creating a cell extension field of type PoW solution. */
   field = trn_extension_field_new();
-  trn_extension_field_set_field_type(field, TRUNNEL_CELL_EXTENSION_TYPE_POW);
+  trn_extension_field_set_field_type(field, TRUNNEL_EXT_TYPE_POW);
 
   /* Build PoW extension field. */
   pow_ext = trn_cell_extension_pow_new();
@@ -399,7 +399,7 @@ build_introduce_pow_extension(const hs_pow_solution_t *pow_solution,
   /* Copy PoW solution values into PoW extension cell. */
 
   /* Equi-X base scheme */
-  trn_cell_extension_pow_set_pow_version(pow_ext, TRUNNEL_POW_EQUIX);
+  trn_cell_extension_pow_set_pow_version(pow_ext, TRUNNEL_POW_VERSION_EQUIX);
 
   memcpy(trn_cell_extension_pow_getarray_pow_nonce(pow_ext),
          &pow_solution->nonce, TRUNNEL_POW_NONCE_LEN);
@@ -793,6 +793,62 @@ hs_cell_parse_intro_established(const uint8_t *payload, size_t payload_len)
   return ret;
 }
 
+/** Parse the cell PoW solution extension. Return 0 on success and data
+ * structure is updated with the PoW effort. Return -1 on any kind of error
+ * including if PoW couldn't be verified. */
+static int
+handle_introduce2_encrypted_cell_pow_extension(const hs_service_t *service,
+                                 const trn_extension_field_t *field,
+                                 hs_cell_introduce2_data_t *data)
+{
+  int ret = -1;
+  trn_cell_extension_pow_t *pow = NULL;
+  hs_pow_solution_t sol;
+
+  tor_assert(field);
+
+  if (trn_cell_extension_pow_parse(&pow,
+               trn_extension_field_getconstarray_field(field),
+               trn_extension_field_getlen_field(field)) < 0) {
+    goto end;
+  }
+
+  /* There is only one version supported at the moment so validate we at least
+   * have that. */
+  if (trn_cell_extension_pow_get_pow_version(pow) !=
+      TRUNNEL_POW_VERSION_EQUIX) {
+    log_debug(LD_REND, "Unsupported PoW version. Malformed INTRODUCE2");
+    goto end;
+  }
+
+  /* Effort E */
+  sol.effort = trn_cell_extension_pow_get_pow_effort(pow);
+  /* Seed C */
+  sol.seed_head = trn_cell_extension_pow_get_pow_seed(pow);
+  /* Nonce N */
+  memcpy(&sol.nonce, trn_cell_extension_pow_getconstarray_pow_nonce(pow),
+         HS_POW_NONCE_LEN);
+  /* Solution S */
+  memcpy(&sol.equix_solution,
+         trn_cell_extension_pow_getconstarray_pow_solution(pow),
+         HS_POW_EQX_SOL_LEN);
+
+  if (hs_pow_verify(service->state.pow_state, &sol)) {
+    log_info(LD_REND, "PoW INTRODUCE2 request failed to verify.");
+    goto end;
+  }
+
+  log_info(LD_REND, "PoW INTRODUCE2 request successfully verified.");
+  data->rdv_data.pow_effort = sol.effort;
+
+  /* Successfully parsed and verified the PoW solution */
+  ret = 0;
+
+ end:
+  trn_cell_extension_pow_free(pow);
+  return ret;
+}
+
 /** For the encrypted INTRO2 cell in <b>encrypted_section</b>, use the crypto
  * material in <b>data</b> to compute the right ntor keys. Also validate the
  * INTRO2 MAC to ensure that the keys are the right ones.
@@ -862,11 +918,15 @@ get_introduce2_keys_and_verify_mac(hs_cell_introduce2_data_t *data,
 }
 
 /** Parse the given INTRODUCE cell extension. Update the data object
- * accordingly depending on the extension. */
-static void
-parse_introduce_cell_extension(hs_cell_introduce2_data_t *data,
+ * accordingly depending on the extension. Return 0 if it validated
+ * correctly, or return -1 if it is malformed (for example because it
+ * includes a PoW that doesn't verify). */
+static int
+parse_introduce_cell_extension(const hs_service_t *service,
+                               hs_cell_introduce2_data_t *data,
                                const trn_extension_field_t *field)
 {
+  int ret = 0;
   trn_extension_field_cc_t *cc_field = NULL;
 
   tor_assert(data);
@@ -879,11 +939,20 @@ parse_introduce_cell_extension(hs_cell_introduce2_data_t *data,
     data->pv.protocols_known = 1;
     data->pv.supports_congestion_control = data->rdv_data.cc_enabled;
     break;
+  case TRUNNEL_EXT_TYPE_POW:
+    /* PoW request. If successful, the effort is put in the data. */
+    if (handle_introduce2_encrypted_cell_pow_extension(service,
+                                                       field, data) < 0) {
+      log_fn(LOG_PROTOCOL_WARN, LD_REND, "Invalid PoW cell extension.");
+      ret = -1;
+    }
+    break;
   default:
     break;
   }
 
   trn_extension_field_cc_free(cc_field);
+  return ret;
 }
 
 /** Parse the INTRODUCE2 cell using data which contains everything we need to
@@ -1026,7 +1095,9 @@ hs_cell_parse_introduce2(hs_cell_introduce2_data_t *data,
         /* The number of extensions should match the number of fields. */
         break;
       }
-      parse_introduce_cell_extension(data, field);
+      if (parse_introduce_cell_extension(service, data, field) < 0) {
+        goto done;
+      }
     }
   }
 
diff --git a/src/feature/hs/hs_cell.h b/src/feature/hs/hs_cell.h
index 61c0a94b20..7b547991e6 100644
--- a/src/feature/hs/hs_cell.h
+++ b/src/feature/hs/hs_cell.h
@@ -60,6 +60,8 @@ typedef struct hs_cell_intro_rdv_data_t {
   smartlist_t *link_specifiers;
   /** Congestion control parameters. */
   unsigned int cc_enabled : 1;
+  /** PoW effort. */
+  uint32_t pow_effort;
 } hs_cell_intro_rdv_data_t;
 
 /** This data structure contains data that we need to parse an INTRODUCE2 cell
diff --git a/src/feature/hs/hs_circuit.c b/src/feature/hs/hs_circuit.c
index 835cd366ad..cbb3e0bfdd 100644
--- a/src/feature/hs/hs_circuit.c
+++ b/src/feature/hs/hs_circuit.c
@@ -310,8 +310,9 @@ get_service_anonymity_string(const hs_service_t *service)
  * MAX_REND_FAILURES then it will give up. */
 MOCK_IMPL(STATIC void,
 launch_rendezvous_point_circuit,(const hs_service_t *service,
-                                 const hs_service_intro_point_t *ip,
-                                 const hs_cell_introduce2_data_t *data))
+                                 const ed25519_public_key_t *ip_auth_pubkey,
+                                 const curve25519_keypair_t *ip_enc_key_kp,
+                                 const hs_cell_intro_rdv_data_t *rdv_data))
 {
   int circ_needs_uptime;
   time_t now = time(NULL);
@@ -319,15 +320,16 @@ launch_rendezvous_point_circuit,(const hs_service_t *service,
   origin_circuit_t *circ;
 
   tor_assert(service);
-  tor_assert(ip);
-  tor_assert(data);
+  tor_assert(ip_auth_pubkey);
+  tor_assert(ip_enc_key_kp);
+  tor_assert(rdv_data);
 
   circ_needs_uptime = hs_service_requires_uptime_circ(service->config.ports);
 
   /* Get the extend info data structure for the chosen rendezvous point
    * specified by the given link specifiers. */
-  info = hs_get_extend_info_from_lspecs(data->rdv_data.link_specifiers,
-                                        &data->rdv_data.onion_pk,
+  info = hs_get_extend_info_from_lspecs(rdv_data->link_specifiers,
+                                        &rdv_data->onion_pk,
                                         service->config.is_single_onion);
   if (info == NULL) {
     /* We are done here, we can't extend to the rendezvous point. */
@@ -375,7 +377,7 @@ launch_rendezvous_point_circuit,(const hs_service_t *service,
                     "for %s service %s",
            safe_str_client(extend_info_describe(info)),
            safe_str_client(hex_str((const char *)
-                                   data->rdv_data.rendezvous_cookie,
+                                   rdv_data->rendezvous_cookie,
                                    REND_COOKIE_LEN)),
            get_service_anonymity_string(service),
            safe_str_client(service->onion_address));
@@ -392,10 +394,10 @@ launch_rendezvous_point_circuit,(const hs_service_t *service,
      * key will be used for the RENDEZVOUS1 cell that will be sent on the
      * circuit once opened. */
     curve25519_keypair_generate(&ephemeral_kp, 0);
-    if (hs_ntor_service_get_rendezvous1_keys(&ip->auth_key_kp.pubkey,
-                                             &ip->enc_key_kp,
+    if (hs_ntor_service_get_rendezvous1_keys(ip_auth_pubkey,
+                                             ip_enc_key_kp,
                                              &ephemeral_kp,
-                                             &data->rdv_data.client_pk,
+                                             &rdv_data->client_pk,
                                              &keys) < 0) {
       /* This should not really happened but just in case, don't make tor
        * freak out, close the circuit and move on. */
@@ -406,7 +408,7 @@ launch_rendezvous_point_circuit,(const hs_service_t *service,
       goto end;
     }
     circ->hs_ident = create_rp_circuit_identifier(service,
-                                       data->rdv_data.rendezvous_cookie,
+                                       rdv_data->rendezvous_cookie,
                                        &ephemeral_kp.pubkey, &keys);
     memwipe(&ephemeral_kp, 0, sizeof(ephemeral_kp));
     memwipe(&keys, 0, sizeof(keys));
@@ -414,7 +416,7 @@ launch_rendezvous_point_circuit,(const hs_service_t *service,
   }
 
   /* Setup congestion control if asked by the client from the INTRO cell. */
-  if (data->rdv_data.cc_enabled) {
+  if (rdv_data->cc_enabled) {
     hs_circ_setup_congestion_control(circ, congestion_control_sendme_inc(),
                                      service->config.is_single_onion);
   }
@@ -600,6 +602,102 @@ cleanup_on_free_client_circ(circuit_t *circ)
    * Thus possible that this passes through. */
 }
 
+/** Return less than 0 if a precedes b, 0 if a equals b and greater than 0 if
+ * b precedes a. */
+static int
+compare_rend_request_by_effort_(const void *_a, const void *_b)
+{
+  const pending_rend_t *a = _a, *b = _b;
+  if (a->rdv_data.pow_effort < b->rdv_data.pow_effort) {
+    return -1;
+  } else if (a->rdv_data.pow_effort == b->rdv_data.pow_effort) {
+    return 0;
+  } else {
+    return 1;
+  }
+}
+
+static void
+handle_rend_pqueue_cb(mainloop_event_t *ev, void *arg)
+{
+  (void) ev; /* Not using the returned event, make compiler happy. */
+  hs_service_t *service = arg;
+  hs_pow_service_state_t *pow_state = service->state.pow_state;
+
+  /* Pop next request by effort. */
+  pending_rend_t *req =
+    smartlist_pqueue_pop(pow_state->rend_request_pqueue,
+                         compare_rend_request_by_effort_,
+                         offsetof(pending_rend_t, idx));
+
+  log_info(LD_REND, "Dequeued pending rendezvous request with effort: %u. "
+                    "Remaining requests: %u",
+           req->rdv_data.pow_effort,
+           smartlist_len(pow_state->rend_request_pqueue));
+
+  /* Launch the rendezvous circuit. */
+  launch_rendezvous_point_circuit(service, &req->ip_auth_pubkey,
+                                  &req->ip_enc_key_kp, &req->rdv_data);
+
+  /* Clean memory. */
+  link_specifier_smartlist_free(req->rdv_data.link_specifiers);
+  memwipe(req, 0, sizeof(pending_rend_t));
+  tor_free(req);
+
+  /* If there are still some pending rendezvous circuits in the pqueue then
+   * reschedule the event in order to continue handling them. */
+  if (smartlist_len(pow_state->rend_request_pqueue) > 0) {
+    mainloop_event_activate(pow_state->pop_pqueue_ev);
+  }
+}
+
+/** HRPR: Given the information needed to launch a rendezvous circuit and an
+ * effort value, enqueue the rendezvous request in the service's PoW priority
+ * queue with the effort being the priority. */
+static void
+enqueue_rend_request(const hs_service_t *service, hs_service_intro_point_t *ip,
+                     hs_cell_introduce2_data_t *data)
+{
+  hs_pow_service_state_t *pow_state = NULL;
+  pending_rend_t *req = NULL;
+
+  tor_assert(service);
+  tor_assert(ip);
+  tor_assert(data);
+
+  /* Ease our lives */
+  pow_state = service->state.pow_state;
+  req = tor_malloc_zero(sizeof(pending_rend_t));
+
+  /* Copy over the rendezvous request the needed data to launch a circuit. */
+  ed25519_pubkey_copy(&req->ip_auth_pubkey, &ip->auth_key_kp.pubkey);
+  memcpy(&req->ip_enc_key_kp, &ip->enc_key_kp, sizeof(req->ip_enc_key_kp));
+  memcpy(&req->rdv_data, &data->rdv_data, sizeof(req->rdv_data));
+  /* Invalidate the link specifier pointer in the introduce2 data so it
+   * doesn't get freed under us. */
+  data->rdv_data.link_specifiers = NULL;
+  req->idx = -1;
+
+  /* Enqueue the rendezvous request. */
+  smartlist_pqueue_add(pow_state->rend_request_pqueue,
+                       compare_rend_request_by_effort_,
+                       offsetof(pending_rend_t, idx), req);
+
+  log_info(LD_REND, "Enqueued rendezvous request with effort: %u. "
+                    "Remaining requests: %u",
+           req->rdv_data.pow_effort,
+           smartlist_len(pow_state->rend_request_pqueue));
+
+  /* Initialize the priority queue event if it hasn't been done so already. */
+  if (pow_state->pop_pqueue_ev == NULL) {
+    pow_state->pop_pqueue_ev =
+        mainloop_event_new(handle_rend_pqueue_cb, (void *)service);
+  }
+
+  /* Activate event, we just enqueued a rendezvous request. */
+  mainloop_event_activate(pow_state->pop_pqueue_ev);
+}
+
 /* ========== */
 /* Public API */
 /* ========== */
@@ -1008,6 +1106,7 @@ hs_circ_handle_introduce2(const hs_service_t *service,
   data.replay_cache = ip->replay_cache;
   data.rdv_data.link_specifiers = smartlist_new();
   data.rdv_data.cc_enabled = 0;
+  data.rdv_data.pow_effort = 0;
 
   if (get_subcredential_for_handling_intro2_cell(service, &data,
                                                  subcredential)) {
@@ -1045,12 +1144,29 @@ hs_circ_handle_introduce2(const hs_service_t *service,
    * so increment our counter that we've seen one on this intro point. */
   ip->introduce2_count++;
 
+  /* Add the rendezvous request to the priority queue if PoW defenses are
+   * enabled, otherwise rendezvous as usual. */
+  if (service->config.has_pow_defenses_enabled) {
+    log_debug(LD_REND, "Adding introduction request to pqueue with effort: %u",
+              data.rdv_data.pow_effort);
+    enqueue_rend_request(service, ip, &data);
+
+    /* Increase the total effort in valid requests received this period. */
+    service->state.pow_state->total_effort += data.rdv_data.pow_effort;
+
+    /* Successfully added rend circuit to priority queue. */
+    ret = 0;
+    goto done;
+  }
+
   /* Launch rendezvous circuit with the onion key and rend cookie. */
-  launch_rendezvous_point_circuit(service, ip, &data);
+  launch_rendezvous_point_circuit(service, &ip->auth_key_kp.pubkey,
+                                  &ip->enc_key_kp, &data.rdv_data);
   /* Success. */
   ret = 0;
 
  done:
+  /* Note that if PoW defenses are enabled, this is NULL. */
   link_specifier_smartlist_free(data.rdv_data.link_specifiers);
   memwipe(&data, 0, sizeof(data));
   return ret;
diff --git a/src/feature/hs/hs_circuit.h b/src/feature/hs/hs_circuit.h
index 3c84abaad2..8f888c569e 100644
--- a/src/feature/hs/hs_circuit.h
+++ b/src/feature/hs/hs_circuit.h
@@ -12,8 +12,23 @@
 #include "core/or/or.h"
 #include "lib/crypt_ops/crypto_ed25519.h"
 
+#include "feature/hs/hs_cell.h"
 #include "feature/hs/hs_service.h"
 
+/* HRPR TODO Putting this here for now... */
+typedef struct pending_rend_t {
+  /* Intro point authentication pubkey. */
+  ed25519_public_key_t ip_auth_pubkey;
+  /* Intro point encryption keypair for the "ntor" type. */
+  curve25519_keypair_t ip_enc_key_kp;
+
+  /* Rendezvous data for the circuit. */
+  hs_cell_intro_rdv_data_t rdv_data;
+
+  /** Position of element in the heap */
+  int idx;
+} pending_rend_t;
+
 /* Cleanup function when the circuit is closed or freed. */
 void hs_circ_cleanup_on_close(circuit_t *circ);
 void hs_circ_cleanup_on_free(circuit_t *circ);
@@ -84,11 +99,11 @@ create_rp_circuit_identifier(const hs_service_t *service,
                              const curve25519_public_key_t *server_pk,
                              const struct hs_ntor_rend_cell_keys_t *keys);
 
-struct hs_cell_introduce2_data_t;
 MOCK_DECL(STATIC void,
 launch_rendezvous_point_circuit,(const hs_service_t *service,
-                                 const hs_service_intro_point_t *ip,
-                                const struct hs_cell_introduce2_data_t *data));
+                                 const ed25519_public_key_t *ip_auth_pubkey,
+                                 const curve25519_keypair_t *ip_enc_key_kp,
+                                 const hs_cell_intro_rdv_data_t *rdv_data));
 
 #endif /* defined(HS_CIRCUIT_PRIVATE) */
 
diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c
index 4a8a758b3f..eb905714c1 100644
--- a/src/test/test_hs_service.c
+++ b/src/test/test_hs_service.c
@@ -2279,12 +2279,14 @@ mock_build_state_get_exit_node(cpath_build_state_t *state)
 
 static void
 mock_launch_rendezvous_point_circuit(const hs_service_t *service,
-                                     const hs_service_intro_point_t *ip,
-                                     const hs_cell_introduce2_data_t *data)
+                             const ed25519_public_key_t *ip_auth_pubkey,
+                             const curve25519_keypair_t *ip_enc_key_kp,
+                             const hs_cell_intro_rdv_data_t *rdv_data)
 {
   (void) service;
-  (void) ip;
-  (void) data;
+  (void) ip_auth_pubkey;
+  (void) ip_enc_key_kp;
+  (void) rdv_data;
   return;
 }
 
diff --git a/src/trunnel/hs/cell_introduce1.h b/src/trunnel/hs/cell_introduce1.h
index 89339e1a0d..90d34f37f2 100644
--- a/src/trunnel/hs/cell_introduce1.h
+++ b/src/trunnel/hs/cell_introduce1.h
@@ -19,10 +19,10 @@ struct link_specifier_st;
 #define TRUNNEL_HS_INTRO_AUTH_KEY_TYPE_LEGACY1 1
 #define TRUNNEL_HS_INTRO_AUTH_KEY_TYPE_ED25519 2
 #define TRUNNEL_HS_INTRO_ONION_KEY_TYPE_NTOR 1
-#define TRUNNEL_CELL_EXTENSION_TYPE_POW 1
+#define TRUNNEL_EXT_TYPE_POW 2
 #define TRUNNEL_POW_NONCE_LEN 16
 #define TRUNNEL_POW_SOLUTION_LEN 16
-#define TRUNNEL_POW_EQUIX 1
+#define TRUNNEL_POW_VERSION_EQUIX 1
 #if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_TRN_CELL_EXTENSION_POW)
 struct trn_cell_extension_pow_st {
   uint8_t pow_version;
diff --git a/src/trunnel/hs/cell_introduce1.trunnel b/src/trunnel/hs/cell_introduce1.trunnel
index 18865ddc02..35e00bed94 100644
--- a/src/trunnel/hs/cell_introduce1.trunnel
+++ b/src/trunnel/hs/cell_introduce1.trunnel
@@ -79,7 +79,7 @@ struct trn_cell_introduce_encrypted {
  */
 
 /* Cell extension type PoW. */
-const TRUNNEL_CELL_EXTENSION_TYPE_POW = 0x01;
+const TRUNNEL_EXT_TYPE_POW = 0x02;
 
 /*
  * HRPR: PoW Solution Extension. Proposal 327.
@@ -88,7 +88,7 @@ const TRUNNEL_CELL_EXTENSION_TYPE_POW = 0x01;
 const TRUNNEL_POW_NONCE_LEN = 16;
 const TRUNNEL_POW_SOLUTION_LEN = 16;
 /* Version 1 is based on Equi-X scheme. */
-const TRUNNEL_POW_EQUIX = 0x01;
+const TRUNNEL_POW_VERSION_EQUIX = 0x01;
 
 struct trn_cell_extension_pow {
   /* Type of PoW system used. */

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the tor-commits mailing list