Browse Source

Compute the description revision counter using the OPE scheme.

To do so for a given descriptor, we use the "seconds since the SR protocol run"
started, for the SRV that is relevant to this descriptor. This is guaranteed to
be a positive value (since we need an SRV to be able to build a descriptor),
and it's also guaranteed to be a small value (since SRVs stop being listed on a
consensus after 48 hours).

We cannot use the "seconds since the time period started", because for the next
descriptor we use the next time period, so the timestamp would end up negative.
See [SERVICEUPLOAD] from rend-spec-v3.txt for more details.

To do so, we have to introduce a new `is_current` argument to a bunch of
functions, because to use "seconds since the SR protocol run" we need to know
if we are building the current or the next descriptor, since we use a different
SRV for each descriptor.
George Kadianakis 6 years ago
parent
commit
05c362274b
2 changed files with 97 additions and 13 deletions
  1. 96 13
      src/or/hs_service.c
  2. 1 0
      src/or/or.h

+ 96 - 13
src/or/hs_service.c

@@ -91,7 +91,8 @@ static smartlist_t *hs_service_staging_list;
 static int consider_republishing_hs_descriptors = 0;
 static int consider_republishing_hs_descriptors = 0;
 
 
 /* Static declaration. */
 /* Static declaration. */
-static void set_descriptor_revision_counter(hs_descriptor_t *hs_desc);
+static void set_descriptor_revision_counter(hs_service_descriptor_t *hs_desc,
+                                            time_t now, bool is_current);
 static void move_descriptors(hs_service_t *src, hs_service_t *dst);
 static void move_descriptors(hs_service_t *src, hs_service_t *dst);
 
 
 /* Helper: Function to compare two objects in the service map. Return 1 if the
 /* Helper: Function to compare two objects in the service map. Return 1 if the
@@ -1420,11 +1421,15 @@ build_service_desc_keys(const hs_service_t *service,
  * the update function. On success, desc_out will point to the newly allocated
  * the update function. On success, desc_out will point to the newly allocated
  * descriptor object.
  * descriptor object.
  *
  *
+ * If <b>is_current</b> is true, this is the current service descriptor,
+ * otherwise it's the next one.
+ *
  * This can error if we are unable to create keys or certificate. */
  * This can error if we are unable to create keys or certificate. */
 static void
 static void
 build_service_descriptor(hs_service_t *service, time_t now,
 build_service_descriptor(hs_service_t *service, time_t now,
                          uint64_t time_period_num,
                          uint64_t time_period_num,
-                         hs_service_descriptor_t **desc_out)
+                         hs_service_descriptor_t **desc_out,
+                         bool is_current)
 {
 {
   char *encoded_desc;
   char *encoded_desc;
   hs_service_descriptor_t *desc;
   hs_service_descriptor_t *desc;
@@ -1449,7 +1454,7 @@ build_service_descriptor(hs_service_t *service, time_t now,
   }
   }
 
 
   /* Set the revision counter for this descriptor */
   /* Set the revision counter for this descriptor */
-  set_descriptor_revision_counter(desc->desc);
+  set_descriptor_revision_counter(desc, now, is_current);
 
 
   /* Let's make sure that we've created a descriptor that can actually be
   /* Let's make sure that we've created a descriptor that can actually be
    * encoded properly. This function also checks if the encoded output is
    * encoded properly. This function also checks if the encoded output is
@@ -1515,9 +1520,9 @@ build_descriptors_for_new_service(hs_service_t *service, time_t now)
 
 
   /* Build descriptors. */
   /* Build descriptors. */
   build_service_descriptor(service, now, current_desc_tp,
   build_service_descriptor(service, now, current_desc_tp,
-                           &service->desc_current);
+                           &service->desc_current, 1);
   build_service_descriptor(service, now, next_desc_tp,
   build_service_descriptor(service, now, next_desc_tp,
-                           &service->desc_next);
+                           &service->desc_next, 0);
   log_info(LD_REND, "Hidden service %s has just started. Both descriptors "
   log_info(LD_REND, "Hidden service %s has just started. Both descriptors "
                     "built. Now scheduled for upload.",
                     "built. Now scheduled for upload.",
            safe_str_client(service->onion_address));
            safe_str_client(service->onion_address));
@@ -1548,7 +1553,7 @@ build_all_descriptors(time_t now)
 
 
     if (service->desc_next == NULL) {
     if (service->desc_next == NULL) {
       build_service_descriptor(service, now, hs_get_next_time_period_num(0),
       build_service_descriptor(service, now, hs_get_next_time_period_num(0),
-                               &service->desc_next);
+                               &service->desc_next, 0);
       log_info(LD_REND, "Hidden service %s next descriptor successfully "
       log_info(LD_REND, "Hidden service %s next descriptor successfully "
                         "built. Now scheduled for upload.",
                         "built. Now scheduled for upload.",
                safe_str_client(service->onion_address));
                safe_str_client(service->onion_address));
@@ -2514,16 +2519,94 @@ increment_descriptor_revision_counter(hs_descriptor_t *hs_desc)
   update_revision_counters_in_state();
   update_revision_counters_in_state();
 }
 }
 
 
-/** Set the revision counter in <b>hs_desc</b>, using the state file to find
- *  the current counter value if it exists. */
+/** Set the revision counter in <b>hs_desc</b>. We do this by encrypting a
+ *  timestamp using an OPE scheme and using the ciphertext as our revision
+ *  counter.
+ *
+ *  If <b>is_current</b> is true, then this is the current HS descriptor,
+ *  otherwise it's the next one. */
 static void
 static void
-set_descriptor_revision_counter(hs_descriptor_t *hs_desc)
+set_descriptor_revision_counter(hs_service_descriptor_t *hs_desc, time_t now,
+                                bool is_current)
 {
 {
-  /* Find stored rev counter if it exists */
-  uint64_t rev_counter =
-    get_rev_counter_for_service(&hs_desc->plaintext_data.blinded_pubkey);
+  uint64_t rev_counter = 0;
 
 
-  hs_desc->plaintext_data.revision_counter = rev_counter;
+  /* Get current time */
+  time_t srv_start = 0;
+
+  /* As our revision counter plaintext value, we use the seconds since the
+   * start of the SR protocol run that is relevant to this descriptor. This is
+   * guaranteed to be a positive value since we need the SRV to start making a
+   * descriptor (so that we know where to upload it).
+   *
+   * Depending on whether we are building the current or the next descriptor,
+   * services use a different SRV value. See [SERVICEUPLOAD] in
+   * rend-spec-v3.txt:
+   *
+   * In particular, for the current descriptor (aka first descriptor), Tor
+   * always uses the previous SRV for uploading the descriptor, and hence we
+   * should use the start time of the previous protocol run here.
+   *
+   * Whereas for the next descriptor (aka second descriptor), Tor always uses
+   * the current SRV for uploading the descriptor.  and hence we use the start
+   * time of the current protocol run.
+   */
+  if (is_current) {
+    srv_start = sr_state_get_start_time_of_previous_protocol_run(now);
+  } else {
+    srv_start = sr_state_get_start_time_of_current_protocol_run(now);
+  }
+
+  log_info(LD_REND, "Setting rev counter for TP #%u: "
+           "SRV started at %d, now %d (%s)",
+           (unsigned) hs_desc->time_period_num, (int)srv_start,
+           (int)now, is_current ? "current" : "next");
+
+  tor_assert_nonfatal(now >= srv_start);
+
+  /* Compute seconds elapsed since the start of the time period. That's the
+   * number of seconds of how long this blinded key has been active. */
+  time_t seconds_since_start_of_srv = now - srv_start;
+
+  /* Increment by one so that we are definitely sure this is strictly
+   * positive and not zero. */
+  seconds_since_start_of_srv++;
+
+  /* Check for too big inputs. */
+  if (BUG(seconds_since_start_of_srv > OPE_INPUT_MAX)) {
+    seconds_since_start_of_srv = OPE_INPUT_MAX;
+  }
+
+  /* Now we compute the actual revision counter value by encrypting the
+     plaintext using an OPE construction: */
+
+  /* First, compute OPE key as: K = H("rev-counter-generation" | S) */
+  uint8_t key[DIGEST256_LEN];
+  {
+    crypto_digest_t *digest = crypto_digest256_new(DIGEST_SHA3_256);
+    const char ope_key_prefix[] = "rev-counter-generation";
+    ed25519_secret_key_t *eph_privkey = &hs_desc->blinded_kp.seckey;
+    crypto_digest_add_bytes(digest, ope_key_prefix, sizeof(ope_key_prefix));
+    crypto_digest_add_bytes(digest, (char*)eph_privkey->seckey,
+                            sizeof(eph_privkey->seckey));
+    crypto_digest_get_digest(digest, (char *)key, sizeof(key));
+    crypto_digest_free(digest);
+  }
+
+  { /* Now encrypt the revision counter! */
+    crypto_ope_t *ope = NULL;
+    ope = crypto_ope_new(key);
+    rev_counter = crypto_ope_encrypt(ope, (int) seconds_since_start_of_srv);
+    crypto_ope_free(ope);
+  }
+
+  /* The OPE module returns UINT64_MAX in case of errors. */
+  tor_assert_nonfatal(rev_counter < UINT64_MAX);
+
+  log_info(LD_REND, "Encrypted revision counter %d to %ld",
+           (int) seconds_since_start_of_srv, (long int) rev_counter);
+
+  hs_desc->desc->plaintext_data.revision_counter = rev_counter;
 }
 }
 
 
 /* Encode and sign the service descriptor desc and upload it to the
 /* Encode and sign the service descriptor desc and upload it to the

+ 1 - 0
src/or/or.h

@@ -79,6 +79,7 @@
 #include "or/replaycache.h"
 #include "or/replaycache.h"
 #include "lib/crypt_ops/crypto_curve25519.h"
 #include "lib/crypt_ops/crypto_curve25519.h"
 #include "lib/crypt_ops/crypto_ed25519.h"
 #include "lib/crypt_ops/crypto_ed25519.h"
+#include "lib/crypt_ops/crypto_ope.h"
 #include "tor_queue.h"
 #include "tor_queue.h"
 #include "common/token_bucket.h"
 #include "common/token_bucket.h"
 #include "common/util_format.h"
 #include "common/util_format.h"