implement DV learn monotime handling
authorChristian Grothoff <christian@grothoff.org>
Thu, 25 Apr 2019 11:44:49 +0000 (13:44 +0200)
committerChristian Grothoff <christian@grothoff.org>
Thu, 25 Apr 2019 11:44:49 +0000 (13:44 +0200)
src/include/gnunet_peerstore_service.h
src/transport/gnunet-service-tng.c

index b20c1f3c77a07ed769d69463a91ddb4169f05175..ad80a3fa3734b3e27cde1d896de398339b8866c9 100644 (file)
@@ -60,6 +60,14 @@ extern "C" {
 #define GNUNET_PEERSTORE_TRANSPORT_BACKCHANNEL_MONOTIME \
   "transport-backchannel-monotonic-time"
 
+/**
+ * Key used to store sender's monotonic time from DV learn
+ * messages.
+ */
+#define GNUNET_PEERSTORE_TRANSPORT_DVLEARN_MONOTIME \
+  "transport-dv-learn-monotonic-time"
+
+
 /**
  * Options for storing values in PEERSTORE
  */
index baf443f036332c257e377ab6c1226b773ef1742b..9fb8141bfaca31c33f2ca6fb0f38339a593fe382 100644 (file)
  *
  * TODO:
  * Implement next:
- * - FIXME: handle_client_send(): pick DVH path, and box
- *   message accordingly (if applicable, see FIXMEs)
- * - proper use/initialization of timestamps in messages exchanged
- *   during DV learning
- * - persistence of monotonic time from DVInit to prevent
- *   replay attacks using DVInit messages
  * - dv hop-by-hop signature verification (at least at initiator)
  * - persistence of monotonic time obtained from other peers
  *   in PEERSTORE (by message type) -- done for backchannel, needed elsewhere?
@@ -719,6 +713,20 @@ struct TransportDVLearnMessage
    */
   struct GNUNET_TIME_RelativeNBO non_network_delay;
 
+  /**
+   * Time at the initiator when generating the signature.
+   *
+   * Note that the receiver MUST IGNORE the absolute time, and only interpret
+   * the value as a mononic time and reject "older" values than the last one
+   * observed.  This is necessary as we do not want to require synchronized
+   * clocks and may not have a bidirectional communication channel.
+   *
+   * Even with this, there is no real guarantee against replay achieved here,
+   * unless the latest timestamp is persisted.  Persistence should be
+   * provided via PEERSTORE if possible.
+   */
+  struct GNUNET_TIME_AbsoluteNBO monotonic_time;
+
   /**
    * Signature of this hop over the path, of purpose
    * #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_INITIATOR
@@ -1663,6 +1671,18 @@ struct Neighbour
    */
   struct GNUNET_SCHEDULER_Task *timeout_task;
 
+  /**
+   * Handle for an operation to fetch @e last_dv_learn_monotime information from
+   * the PEERSTORE, or NULL.
+   */
+  struct GNUNET_PEERSTORE_IterateContext *get;
+
+  /**
+   * Handle to a PEERSTORE store operation to store this @e pid's @e
+   * @e last_dv_learn_monotime.  NULL if no PEERSTORE operation is pending.
+   */
+  struct GNUNET_PEERSTORE_StoreContext *sc;
+
   /**
    * Quota at which CORE is allowed to transmit to this peer
    * (note that the value CORE should actually be told is this
@@ -1677,6 +1697,12 @@ struct Neighbour
    */
   struct GNUNET_BANDWIDTH_Value32NBO quota_out;
 
+  /**
+   * Latest DVLearn monotonic time seen from this peer.  Initialized only
+   * if @e dl_monotime_available is #GNUNET_YES.
+   */
+  struct GNUNET_TIME_Absolute last_dv_learn_monotime;
+
   /**
    * What is the earliest timeout of any message in @e pending_msg_tail?
    */
@@ -1687,6 +1713,12 @@ struct Neighbour
    * CORE?
    */
   int core_visible;
+
+  /**
+   * Do we have the lastest value for @e last_dv_learn_monotime from
+   * PEERSTORE yet, or are we still waiting for a reply of PEERSTORE?
+   */
+  int dv_monotime_available;
 };
 
 
@@ -2886,7 +2918,20 @@ free_neighbour (struct Neighbour *neighbour)
       free_dv_route (dv);
   }
   if (NULL != neighbour->reassembly_timeout_task)
+  {
     GNUNET_SCHEDULER_cancel (neighbour->reassembly_timeout_task);
+    neighbour->reassembly_timeout_task = NULL;
+  }
+  if (NULL != neighbour->get)
+  {
+    GNUNET_PEERSTORE_iterate_cancel (neighbour->get);
+    neighbour->get = NULL;
+  }
+  if (NULL != neighbour->sc)
+  {
+    GNUNET_PEERSTORE_store_cancel (neighbour->sc);
+    neighbour->sc = NULL;
+  }
   GNUNET_free (neighbour);
 }
 
@@ -6004,6 +6049,7 @@ forward_dv_learn (const struct GNUNET_PeerIdentity *next_hop,
 /**
  * Check signature of type #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_INITIATOR
  *
+ * @param sender_monotonic_time monotonic time of the initiator
  * @param init the signer
  * @param challenge the challenge that was signed
  * @param init_sig signature presumably by @a init
@@ -6011,6 +6057,7 @@ forward_dv_learn (const struct GNUNET_PeerIdentity *next_hop,
  */
 static int
 validate_dv_initiator_signature (
+  struct GNUNET_TIME_AbsoluteNBO sender_monotonic_time,
   const struct GNUNET_PeerIdentity *init,
   const struct ChallengeNonceP *challenge,
   const struct GNUNET_CRYPTO_EddsaSignature *init_sig)
@@ -6018,6 +6065,7 @@ validate_dv_initiator_signature (
   struct DvInitPS ip = {.purpose.purpose = htonl (
                           GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_INITIATOR),
                         .purpose.size = htonl (sizeof (ip)),
+                        .monotonic_time = sender_monotonic_time,
                         .challenge = *challenge};
 
   if (
@@ -6223,6 +6271,24 @@ calculate_fork_degree (unsigned int hops_taken,
 }
 
 
+/**
+ * Function called when peerstore is done storing a DV monotonic time.
+ *
+ * @param cls a `struct Neighbour`
+ * @param success #GNUNET_YES if peerstore was successful
+ */
+static void
+neighbour_store_dvmono_cb (void *cls, int success)
+{
+  struct Neighbour *n = cls;
+
+  n->sc = NULL;
+  if (GNUNET_YES != success)
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Failed to store other peer's monotonic time in peerstore!\n");
+}
+
+
 /**
  * Communicator gave us a DV learn message.  Process the request.
  *
@@ -6242,6 +6308,7 @@ handle_dv_learn (void *cls, const struct TransportDVLearnMessage *dvl)
   int do_fwd;
   int did_initiator;
   struct GNUNET_TIME_Absolute in_time;
+  struct Neighbour *n;
 
   nhops = ntohs (dvl->bidirectional); /* 0 = sender is initiator */
   bi_history = ntohs (dvl->bidirectional);
@@ -6276,15 +6343,44 @@ handle_dv_learn (void *cls, const struct TransportDVLearnMessage *dvl)
   /* continue communicator here, everything else can happen asynchronous! */
   finish_cmc_handling (cmc);
 
-  /* OPTIMIZE-FIXME: Technically, we only need to bother checking
-     the initiator signature if we send the message back to the initiator...
-   */
-  if (GNUNET_OK != validate_dv_initiator_signature (&dvl->initiator,
-                                                    &dvl->challenge,
-                                                    &dvl->init_sig))
+  n = lookup_neighbour (&dvl->initiator);
+  if (NULL != n)
   {
-    GNUNET_break_op (0);
-    return;
+    if ((n->dv_monotime_available == GNUNET_YES) &&
+        (GNUNET_TIME_absolute_ntoh (dvl->monotonic_time).abs_value_us <
+         n->last_dv_learn_monotime.abs_value_us))
+    {
+      GNUNET_STATISTICS_update (GST_stats,
+                                "# DV learn discarded due to time travel",
+                                1,
+                                GNUNET_NO);
+      return;
+    }
+    if (GNUNET_OK != validate_dv_initiator_signature (dvl->monotonic_time,
+                                                      &dvl->initiator,
+                                                      &dvl->challenge,
+                                                      &dvl->init_sig))
+    {
+      GNUNET_break_op (0);
+      return;
+    }
+    n->last_dv_learn_monotime = GNUNET_TIME_absolute_ntoh (dvl->monotonic_time);
+    if (GNUNET_YES == n->dv_monotime_available)
+    {
+      if (NULL != n->sc)
+        GNUNET_PEERSTORE_store_cancel (n->sc);
+      n->sc =
+        GNUNET_PEERSTORE_store (peerstore,
+                                "transport",
+                                &dvl->initiator,
+                                GNUNET_PEERSTORE_TRANSPORT_DVLEARN_MONOTIME,
+                                &dvl->monotonic_time,
+                                sizeof (dvl->monotonic_time),
+                                GNUNET_TIME_UNIT_FOREVER_ABS,
+                                GNUNET_PEERSTORE_STOREOPTION_REPLACE,
+                                &neighbour_store_dvmono_cb,
+                                n);
+    }
   }
   // FIXME: asynchronously (!) verify hop-by-hop signatures!
   // => if signature verification load too high, implement random drop
@@ -8057,10 +8153,13 @@ start_dv_learn (void *cls)
   dvl.num_hops = htons (0);
   dvl.bidirectional = htons (0);
   dvl.non_network_delay = GNUNET_TIME_relative_hton (GNUNET_TIME_UNIT_ZERO);
+  dvl.monotonic_time =
+    GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get_monotonic (GST_cfg));
   {
     struct DvInitPS dvip = {.purpose.purpose = htonl (
                               GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_INITIATOR),
                             .purpose.size = htonl (sizeof (dvip)),
+                            .monotonic_time = dvl.monotonic_time,
                             .challenge = lle->challenge};
 
     GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_eddsa_sign (GST_my_private_key,
@@ -8121,6 +8220,44 @@ check_validation_request_pending (void *cls,
 }
 
 
+/**
+ * Function called with the monotonic time of a DV initiator
+ * by PEERSTORE. Updates the time.
+ *
+ * @param cls a `struct Neighbour`
+ * @param record the information found, NULL for the last call
+ * @param emsg error message
+ */
+static void
+neighbour_dv_monotime_cb (void *cls,
+                          const struct GNUNET_PEERSTORE_Record *record,
+                          const char *emsg)
+{
+  struct Neighbour *n = cls;
+  struct GNUNET_TIME_AbsoluteNBO *mtbe;
+  struct GNUNET_TIME_Absolute mt;
+
+  (void) emsg;
+  if (NULL == record)
+  {
+    /* we're done with #neighbour_dv_monotime_cb() invocations,
+       continue normal processing */
+    n->get = NULL;
+    n->dv_monotime_available = GNUNET_YES;
+    return;
+  }
+  if (sizeof (*mtbe) != record->value_size)
+  {
+    GNUNET_break (0);
+    return;
+  }
+  mtbe = record->value;
+  n->last_dv_learn_monotime =
+    GNUNET_TIME_absolute_max (n->last_dv_learn_monotime,
+                              GNUNET_TIME_absolute_ntoh (*mtbe));
+}
+
+
 /**
  * New queue became available.  Process the request.
  *
@@ -8157,6 +8294,13 @@ handle_add_queue_message (void *cls,
                      &neighbour->pid,
                      neighbour,
                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+    neighbour->get =
+      GNUNET_PEERSTORE_iterate (peerstore,
+                                "transport",
+                                &neighbour->pid,
+                                GNUNET_PEERSTORE_TRANSPORT_DVLEARN_MONOTIME,
+                                &neighbour_dv_monotime_cb,
+                                neighbour);
   }
   addr_len = ntohs (aqm->header.size) - sizeof (*aqm);
   addr = (const char *) &aqm[1];