*
* 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?
*/
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
*/
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
*/
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?
*/
* 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;
};
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);
}
/**
* 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
*/
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)
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 (
}
+/**
+ * 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.
*
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);
/* 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
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,
}
+/**
+ * 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.
*
&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];