avoid failing hard if 'gnunetcheck' db does not exist
[oweals/gnunet.git] / src / cadet / gnunet-service-cadet_tunnels.c
index 28004debc5d5e6780d0c0bc8aa6ebe67d6962efe..ad4ed6e96fc36c862ac5211fd2a01f29006825b8 100644 (file)
@@ -1,21 +1,21 @@
 /*
      This file is part of GNUnet.
-     Copyright (C) 2013, 2017 GNUnet e.V.
+     Copyright (C) 2013, 2017, 2018 GNUnet e.V.
 
-     GNUnet is free software; you can redistribute it and/or modify
-     it under the terms of the GNU General Public License as published
-     by the Free Software Foundation; either version 3, or (at your
-     option) any later version.
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
      WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-     General Public License for more details.
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-     You should have received a copy of the GNU General Public License
-     along with GNUnet; see the file COPYING.  If not, write to the
-     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-     Boston, MA 02110-1301, USA.
+     SPDX-License-Identifier: AGPL3.0-or-later
 */
 /**
  * @file cadet/gnunet-service-cadet_tunnels.c
@@ -186,6 +186,12 @@ struct CadetTunnelAxolotl
    */
   struct GNUNET_CRYPTO_EcdhePublicKey DHRr;
 
+  /**
+   * Last ephemeral public key received from the other peer,
+   * for duplicate detection.
+   */
+  struct GNUNET_CRYPTO_EcdhePublicKey last_ephemeral;
+
   /**
    * Time when the current ratchet expires and a new one is triggered
    * (if @e ratchet_allowed is #GNUNET_YES).
@@ -452,6 +458,29 @@ struct CadetTunnel
 };
 
 
+/**
+ * Am I Alice or Betty (some call her Bob), or talking to myself?
+ *
+ * @param other the other peer
+ * @return #GNUNET_YES for Alice, #GNUNET_NO for Betty, #GNUNET_SYSERR if talking to myself
+ */
+static int
+alice_or_betty (const struct GNUNET_PeerIdentity *other)
+{
+  if (0 > GNUNET_CRYPTO_cmp_peer_identity (&my_full_id,
+                                           other))
+    return GNUNET_YES;
+  else if (0 < GNUNET_CRYPTO_cmp_peer_identity (&my_full_id,
+                                                other))
+    return GNUNET_NO;
+  else
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+}
+
+
 /**
  * Connection @a ct is now unready, clear it's ready flag
  * and move it from the ready DLL to the busy DLL.
@@ -1318,6 +1347,8 @@ send_kx (struct CadetTunnel *t,
   struct GNUNET_CADET_TunnelKeyExchangeMessage *msg;
   enum GNUNET_CADET_KX_Flags flags;
 
+  if (GNUNET_YES != alice_or_betty (GCP_get_id (t->destination)))
+    return; /* only Alice may send KX */
   if ( (NULL == ct) ||
        (GNUNET_NO == ct->is_ready) )
     ct = get_ready_connection (t);
@@ -1331,11 +1362,6 @@ send_kx (struct CadetTunnel *t,
     return;
   }
   cc = ct->cc;
-  LOG (GNUNET_ERROR_TYPE_DEBUG,
-       "Sending KX on %s via %s in state %s\n",
-       GCT_2s (t),
-       GCC_2s (cc),
-       estate2s (t->estate));
   env = GNUNET_MQ_msg (msg,
                        GNUNET_MESSAGE_TYPE_CADET_TUNNEL_KX);
   flags = GNUNET_CADET_KX_FLAG_FORCE_REPLY; /* always for KX */
@@ -1343,6 +1369,15 @@ send_kx (struct CadetTunnel *t,
   msg->cid = *GCC_get_id (cc);
   GNUNET_CRYPTO_ecdhe_key_get_public (&ax->kx_0,
                                       &msg->ephemeral_key);
+#if DEBUG_KX
+  msg->ephemeral_key_XXX = ax->kx_0;
+  msg->private_key_XXX = *my_private_key;
+#endif
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Sending KX message to %s with ephemeral %s on CID %s\n",
+       GCT_2s (t),
+       GNUNET_e2s (&msg->ephemeral_key),
+       GNUNET_sh2s (&msg->cid.connection_of_tunnel));
   GNUNET_CRYPTO_ecdhe_key_get_public (&ax->DHRs,
                                       &msg->ratchet_key);
   mark_connection_unready (ct);
@@ -1356,6 +1391,10 @@ send_kx (struct CadetTunnel *t,
                        CADET_TUNNEL_KEY_AX_SENT_AND_RECV);
   GCC_transmit (cc,
                 env);
+  GNUNET_STATISTICS_update (stats,
+                            "# KX transmitted",
+                            1,
+                            GNUNET_NO);
 }
 
 
@@ -1394,11 +1433,6 @@ send_kx_auth (struct CadetTunnel *t,
   }
   t->kx_auth_requested = GNUNET_NO; /* clear flag */
   cc = ct->cc;
-  LOG (GNUNET_ERROR_TYPE_DEBUG,
-       "Sending KX_AUTH on %s using %s\n",
-       GCT_2s (t),
-       GCC_2s (ct->cc));
-
   env = GNUNET_MQ_msg (msg,
                        GNUNET_MESSAGE_TYPE_CADET_TUNNEL_KX_AUTH);
   flags = GNUNET_CADET_KX_FLAG_NONE;
@@ -1410,11 +1444,21 @@ send_kx_auth (struct CadetTunnel *t,
                                       &msg->kx.ephemeral_key);
   GNUNET_CRYPTO_ecdhe_key_get_public (&ax->DHRs,
                                       &msg->kx.ratchet_key);
+#if DEBUG_KX
+  msg->kx.ephemeral_key_XXX = ax->kx_0;
+  msg->kx.private_key_XXX = *my_private_key;
+  msg->r_ephemeral_key_XXX = ax->last_ephemeral;
+#endif
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Sending KX_AUTH message to %s with ephemeral %s on CID %s\n",
+       GCT_2s (t),
+       GNUNET_e2s (&msg->kx.ephemeral_key),
+       GNUNET_sh2s (&msg->kx.cid.connection_of_tunnel));
+
   /* Compute authenticator (this is the main difference to #send_kx()) */
   GNUNET_CRYPTO_hash (&ax->RK,
                       sizeof (ax->RK),
                       &msg->auth);
-
   /* Compute when to be triggered again; actual job will
      be scheduled via #connection_ready_cb() */
   t->kx_retry_delay
@@ -1429,9 +1473,12 @@ send_kx_auth (struct CadetTunnel *t,
   if (CADET_TUNNEL_KEY_OK != t->estate)
     GCT_change_estate (t,
                        CADET_TUNNEL_KEY_AX_AUTH_SENT);
-
   GCC_transmit (cc,
                 env);
+  GNUNET_STATISTICS_update (stats,
+                            "# KX_AUTH transmitted",
+                            1,
+                            GNUNET_NO);
 }
 
 
@@ -1476,66 +1523,57 @@ update_ax_by_kx (struct CadetTunnelAxolotl *ax,
   const char salt[] = "CADET Axolotl salt";
   int am_I_alice;
 
-  if (0 > GNUNET_CRYPTO_cmp_peer_identity (&my_full_id,
-                                           pid))
-    am_I_alice = GNUNET_YES;
-  else if (0 < GNUNET_CRYPTO_cmp_peer_identity (&my_full_id,
-                                                pid))
-    am_I_alice = GNUNET_NO;
-  else
+  if (GNUNET_SYSERR == (am_I_alice = alice_or_betty (pid)))
   {
     GNUNET_break_op (0);
     return GNUNET_SYSERR;
   }
-
   if (0 == memcmp (&ax->DHRr,
                    ratchet_key,
                    sizeof (*ratchet_key)))
   {
+    GNUNET_STATISTICS_update (stats,
+                              "# Ratchet key already known",
+                              1,
+                              GNUNET_NO);
     LOG (GNUNET_ERROR_TYPE_DEBUG,
          "Ratchet key already known. Ignoring KX.\n");
     return GNUNET_NO;
   }
 
   ax->DHRr = *ratchet_key;
-
+  ax->last_ephemeral = *ephemeral_key;
   /* ECDH A B0 */
   if (GNUNET_YES == am_I_alice)
   {
-    GNUNET_CRYPTO_eddsa_ecdh (my_private_key,      /* A */
-                              ephemeral_key, /* B0 */
+    GNUNET_CRYPTO_eddsa_ecdh (my_private_key,      /* a */
+                              ephemeral_key,       /* B0 */
                               &key_material[0]);
   }
   else
   {
-    GNUNET_CRYPTO_ecdh_eddsa (&ax->kx_0,            /* B0 */
-                              &pid->public_key,    /* A */
+    GNUNET_CRYPTO_ecdh_eddsa (&ax->kx_0,            /* b0 */
+                              &pid->public_key,     /* A */
                               &key_material[0]);
   }
-
   /* ECDH A0 B */
   if (GNUNET_YES == am_I_alice)
   {
-    GNUNET_CRYPTO_ecdh_eddsa (&ax->kx_0,            /* A0 */
-                              &pid->public_key,    /* B */
+    GNUNET_CRYPTO_ecdh_eddsa (&ax->kx_0,            /* a0 */
+                              &pid->public_key,     /* B */
                               &key_material[1]);
   }
   else
   {
-    GNUNET_CRYPTO_eddsa_ecdh (my_private_key,      /* A */
-                              ephemeral_key, /* B0 */
+    GNUNET_CRYPTO_eddsa_ecdh (my_private_key,      /*  */
+                              ephemeral_key,       /* A0 */
                               &key_material[1]);
-
-
   }
 
   /* ECDH A0 B0 */
-  /* (This is the triple-DH, we could probably safely skip this,
-     as A0/B0 are already in the key material.) */
-  GNUNET_CRYPTO_ecc_ecdh (&ax->kx_0,             /* A0 or B0 */
-                          ephemeral_key,  /* B0 or A0 */
+  GNUNET_CRYPTO_ecc_ecdh (&ax->kx_0,             /* a0 or b0 */
+                          ephemeral_key,         /* B0 or A0 */
                           &key_material[2]);
-
   /* KDF */
   GNUNET_CRYPTO_kdf (keys, sizeof (keys),
                      salt, sizeof (salt),
@@ -1547,7 +1585,11 @@ update_ax_by_kx (struct CadetTunnelAxolotl *ax,
                    sizeof (ax->RK)))
   {
     LOG (GNUNET_ERROR_TYPE_DEBUG,
-         "Root key of handshake already known. Ignoring KX.\n");
+         "Root key already known. Ignoring KX.\n");
+    GNUNET_STATISTICS_update (stats,
+                              "# Root key already known",
+                              1,
+                              GNUNET_NO);
     return GNUNET_NO;
   }
 
@@ -1592,75 +1634,75 @@ retry_kx (void *cls)
        GCT_2s (t),
        estate2s (t->estate));
   switch (t->estate)
-  {
-  case CADET_TUNNEL_KEY_UNINITIALIZED: /* first attempt */
-  case CADET_TUNNEL_KEY_AX_SENT:       /* trying again */
-    send_kx (t,
-             NULL,
-             &t->ax);
-    break;
-  case CADET_TUNNEL_KEY_AX_RECV:
-  case CADET_TUNNEL_KEY_AX_SENT_AND_RECV:
-    /* We are responding, so only require reply
-       if WE have a channel waiting. */
-    if (NULL != t->unverified_ax)
-    {
-      /* Send AX_AUTH so we might get this one verified */
-      ax = t->unverified_ax;
-    }
-    else
-    {
-      /* How can this be? */
-      GNUNET_break (0);
-      ax = &t->ax;
-    }
-    send_kx_auth (t,
-                  NULL,
-                  ax,
-                  (0 == GCT_count_channels (t))
-                  ? GNUNET_NO
-                  : GNUNET_YES);
-    break;
-  case CADET_TUNNEL_KEY_AX_AUTH_SENT:
-    /* We are responding, so only require reply
-       if WE have a channel waiting. */
-    if (NULL != t->unverified_ax)
-    {
-      /* Send AX_AUTH so we might get this one verified */
-      ax = t->unverified_ax;
-    }
-    else
-    {
-      /* How can this be? */
-      GNUNET_break (0);
-      ax = &t->ax;
-    }
-    send_kx_auth (t,
-                  NULL,
-                  ax,
-                  (0 == GCT_count_channels (t))
-                  ? GNUNET_NO
-                  : GNUNET_YES);
-    break;
-  case CADET_TUNNEL_KEY_OK:
-    /* Must have been the *other* peer asking us to
-       respond with a KX_AUTH. */
-    if (NULL != t->unverified_ax)
-    {
-      /* Sending AX_AUTH in response to AX so we might get this one verified */
-      ax = t->unverified_ax;
-    }
-    else
     {
-      /* Sending AX_AUTH in response to AX_AUTH */
-      ax = &t->ax;
+    case CADET_TUNNEL_KEY_UNINITIALIZED: /* first attempt */
+    case CADET_TUNNEL_KEY_AX_SENT:       /* trying again */
+      send_kx (t,
+               NULL,
+               &t->ax);
+      break;
+    case CADET_TUNNEL_KEY_AX_RECV:
+    case CADET_TUNNEL_KEY_AX_SENT_AND_RECV:
+      /* We are responding, so only require reply
+         if WE have a channel waiting. */
+      if (NULL != t->unverified_ax)
+        {
+          /* Send AX_AUTH so we might get this one verified */
+          ax = t->unverified_ax;
+        }
+      else
+        {
+          /* How can this be? */
+          GNUNET_break (0);
+          ax = &t->ax;
+        }
+      send_kx_auth (t,
+                    NULL,
+                    ax,
+                    (0 == GCT_count_channels (t))
+                    ? GNUNET_NO
+                    : GNUNET_YES);
+      break;
+    case CADET_TUNNEL_KEY_AX_AUTH_SENT:
+      /* We are responding, so only require reply
+         if WE have a channel waiting. */
+      if (NULL != t->unverified_ax)
+        {
+          /* Send AX_AUTH so we might get this one verified */
+          ax = t->unverified_ax;
+        }
+      else
+        {
+          /* How can this be? */
+          GNUNET_break (0);
+          ax = &t->ax;
+        }
+      send_kx_auth (t,
+                    NULL,
+                    ax,
+                    (0 == GCT_count_channels (t))
+                    ? GNUNET_NO
+                    : GNUNET_YES);
+      break;
+    case CADET_TUNNEL_KEY_OK:
+      /* Must have been the *other* peer asking us to
+         respond with a KX_AUTH. */
+      if (NULL != t->unverified_ax)
+        {
+          /* Sending AX_AUTH in response to AX so we might get this one verified */
+          ax = t->unverified_ax;
+        }
+      else
+        {
+          /* Sending AX_AUTH in response to AX_AUTH */
+          ax = &t->ax;
+        }
+      send_kx_auth (t,
+                    NULL,
+                    ax,
+                    GNUNET_NO);
+      break;
     }
-    send_kx_auth (t,
-                  NULL,
-                  ax,
-                  GNUNET_NO);
-    break;
-  }
 }
 
 
@@ -1677,76 +1719,117 @@ GCT_handle_kx (struct CadetTConnection *ct,
                const struct GNUNET_CADET_TunnelKeyExchangeMessage *msg)
 {
   struct CadetTunnel *t = ct->t;
-  struct CadetTunnelAxolotl *ax;
   int ret;
 
-  if (0 ==
-      memcmp (&t->ax.DHRr,
-              &msg->ratchet_key,
-              sizeof (msg->ratchet_key)))
+  GNUNET_STATISTICS_update (stats,
+                            "# KX received",
+                            1,
+                            GNUNET_NO);
+  if (GNUNET_YES ==
+      alice_or_betty (GCP_get_id (t->destination)))
   {
-    LOG (GNUNET_ERROR_TYPE_DEBUG,
-         "Got duplicate KX. Firing back KX_AUTH.\n");
-    send_kx_auth (t,
-                  ct,
-                  &t->ax,
-                  GNUNET_NO);
+    /* Betty/Bob is not allowed to send KX! */
+    GNUNET_break_op (0);
     return;
   }
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Received KX message from %s with ephemeral %s from %s on connection %s\n",
+       GCT_2s (t),
+       GNUNET_e2s (&msg->ephemeral_key),
+       GNUNET_i2s (GCP_get_id (t->destination)),
+       GCC_2s (ct->cc));
+#if 1
+  if ( (0 ==
+        memcmp (&t->ax.DHRr,
+                &msg->ratchet_key,
+                sizeof (msg->ratchet_key))) &&
+       (0 ==
+        memcmp (&t->ax.last_ephemeral,
+                &msg->ephemeral_key,
+                sizeof (msg->ephemeral_key))) )
 
+    {
+      GNUNET_STATISTICS_update (stats,
+                                "# Duplicate KX received",
+                                1,
+                                GNUNET_NO);
+      send_kx_auth (t,
+                    ct,
+                    &t->ax,
+                    GNUNET_NO);
+      return;
+    }
+#endif
   /* We only keep ONE unverified KX around, so if there is an existing one,
      clean it up. */
   if (NULL != t->unverified_ax)
   {
-    if (0 ==
-        memcmp (&t->unverified_ax->DHRr,
-                &msg->ratchet_key,
-                sizeof (msg->ratchet_key)))
+    if ( (0 ==
+          memcmp (&t->unverified_ax->DHRr,
+                  &msg->ratchet_key,
+                  sizeof (msg->ratchet_key))) &&
+         (0 ==
+          memcmp (&t->unverified_ax->last_ephemeral,
+                  &msg->ephemeral_key,
+                  sizeof (msg->ephemeral_key))) )
     {
-      LOG (GNUNET_ERROR_TYPE_DEBUG,
-           "Got duplicate unverified KX on %s. Fire back KX_AUTH again.\n",
-           GCT_2s (t));
+      GNUNET_STATISTICS_update (stats,
+                                "# Duplicate unverified KX received",
+                                1,
+                                GNUNET_NO);
+#if 1
       send_kx_auth (t,
                     ct,
                     t->unverified_ax,
                     GNUNET_NO);
       return;
+#endif
     }
     LOG (GNUNET_ERROR_TYPE_DEBUG,
-         "Dropping old unverified KX state. Got a fresh KX for %s.\n",
-         GCT_2s (t));
+         "Dropping old unverified KX state.\n");
+    GNUNET_STATISTICS_update (stats,
+                              "# Unverified KX dropped for fresh KX",
+                              1,
+                              GNUNET_NO);
+    GNUNET_break (NULL == t->unverified_ax->skipped_head);
     memset (t->unverified_ax,
             0,
             sizeof (struct CadetTunnelAxolotl));
-    t->unverified_ax->DHRs = t->ax.DHRs;
-    t->unverified_ax->kx_0 = t->ax.kx_0;
   }
   else
   {
     LOG (GNUNET_ERROR_TYPE_DEBUG,
-         "Creating fresh unverified KX for %s.\n",
+         "Creating fresh unverified KX for %s\n",
          GCT_2s (t));
+    GNUNET_STATISTICS_update (stats,
+                              "# Fresh KX setup",
+                              1,
+                              GNUNET_NO);
     t->unverified_ax = GNUNET_new (struct CadetTunnelAxolotl);
-    t->unverified_ax->DHRs = t->ax.DHRs;
-    t->unverified_ax->kx_0 = t->ax.kx_0;
   }
   /* Set as the 'current' RK/DHRr the one we are currently using,
      so that the duplicate-detection logic of
      #update_ax_by_kx can work. */
   t->unverified_ax->RK = t->ax.RK;
   t->unverified_ax->DHRr = t->ax.DHRr;
+  t->unverified_ax->DHRs = t->ax.DHRs;
+  t->unverified_ax->kx_0 = t->ax.kx_0;
   t->unverified_attempts = 0;
-  ax = t->unverified_ax;
 
   /* Update 'ax' by the new key material */
-  ret = update_ax_by_kx (ax,
+  ret = update_ax_by_kx (t->unverified_ax,
                          GCP_get_id (t->destination),
                          &msg->ephemeral_key,
                          &msg->ratchet_key);
   GNUNET_break (GNUNET_SYSERR != ret);
   if (GNUNET_OK != ret)
+  {
+    GNUNET_STATISTICS_update (stats,
+                              "# Useless KX",
+                              1,
+                              GNUNET_NO);
     return; /* duplicate KX, nothing to do */
-
+  }
   /* move ahead in our state machine */
   if (CADET_TUNNEL_KEY_UNINITIALIZED == t->estate)
     GCT_change_estate (t,
@@ -1767,6 +1850,75 @@ GCT_handle_kx (struct CadetTConnection *ct,
 }
 
 
+#if DEBUG_KX
+static void
+check_ee (const struct GNUNET_CRYPTO_EcdhePrivateKey *e1,
+          const struct GNUNET_CRYPTO_EcdhePrivateKey *e2)
+{
+  struct GNUNET_CRYPTO_EcdhePublicKey p1;
+  struct GNUNET_CRYPTO_EcdhePublicKey p2;
+  struct GNUNET_HashCode hc1;
+  struct GNUNET_HashCode hc2;
+
+  GNUNET_CRYPTO_ecdhe_key_get_public (e1,
+                                      &p1);
+  GNUNET_CRYPTO_ecdhe_key_get_public (e2,
+                                      &p2);
+  GNUNET_assert (GNUNET_OK ==
+                 GNUNET_CRYPTO_ecc_ecdh (e1,
+                                         &p2,
+                                         &hc1));
+  GNUNET_assert (GNUNET_OK ==
+                 GNUNET_CRYPTO_ecc_ecdh (e2,
+                                         &p1,
+                                         &hc2));
+  GNUNET_break (0 == memcmp (&hc1,
+                             &hc2,
+                             sizeof (hc1)));
+}
+
+
+static void
+check_ed (const struct GNUNET_CRYPTO_EcdhePrivateKey *e1,
+          const struct GNUNET_CRYPTO_EddsaPrivateKey *e2)
+{
+  struct GNUNET_CRYPTO_EcdhePublicKey p1;
+  struct GNUNET_CRYPTO_EddsaPublicKey p2;
+  struct GNUNET_HashCode hc1;
+  struct GNUNET_HashCode hc2;
+
+  GNUNET_CRYPTO_ecdhe_key_get_public (e1,
+                                      &p1);
+  GNUNET_CRYPTO_eddsa_key_get_public (e2,
+                                      &p2);
+  GNUNET_assert (GNUNET_OK ==
+                 GNUNET_CRYPTO_ecdh_eddsa (e1,
+                                           &p2,
+                                           &hc1));
+  GNUNET_assert (GNUNET_OK ==
+                 GNUNET_CRYPTO_eddsa_ecdh (e2,
+                                           &p1,
+                                           &hc2));
+  GNUNET_break (0 == memcmp (&hc1,
+                             &hc2,
+                             sizeof (hc1)));
+}
+
+
+static void
+test_crypto_bug (const struct GNUNET_CRYPTO_EcdhePrivateKey *e1,
+                 const struct GNUNET_CRYPTO_EcdhePrivateKey *e2,
+                 const struct GNUNET_CRYPTO_EddsaPrivateKey *d1,
+                 const struct GNUNET_CRYPTO_EddsaPrivateKey *d2)
+{
+  check_ee (e1, e2);
+  check_ed (e1, d2);
+  check_ed (e2, d1);
+}
+
+#endif
+
+
 /**
  * Handle KX_AUTH message.
  *
@@ -1782,6 +1934,10 @@ GCT_handle_kx_auth (struct CadetTConnection *ct,
   struct GNUNET_HashCode kx_auth;
   int ret;
 
+  GNUNET_STATISTICS_update (stats,
+                            "# KX_AUTH received",
+                            1,
+                            GNUNET_NO);
   if ( (CADET_TUNNEL_KEY_UNINITIALIZED == t->estate) ||
        (CADET_TUNNEL_KEY_AX_RECV == t->estate) )
   {
@@ -1792,9 +1948,9 @@ GCT_handle_kx_auth (struct CadetTConnection *ct,
     return;
   }
   LOG (GNUNET_ERROR_TYPE_DEBUG,
-       "Handling KX_AUTH message for %s\n",
-       GCT_2s (t));
-
+       "Handling KX_AUTH message from %s with ephemeral %s\n",
+       GCT_2s (t),
+       GNUNET_e2s (&msg->kx.ephemeral_key));
   /* We do everything in ax_tmp until we've checked the authentication
      so we don't clobber anything we care about by accident. */
   ax_tmp = t->ax;
@@ -1828,9 +1984,39 @@ GCT_handle_kx_auth (struct CadetTConnection *ct,
                               "# KX_AUTH not using our last KX received (auth failure)",
                               1,
                               GNUNET_NO);
-    send_kx (t,
-             ct,
-             &t->ax);
+    LOG (GNUNET_ERROR_TYPE_WARNING,
+         "KX AUTH mismatch!\n");
+#if DEBUG_KX
+    {
+      struct GNUNET_CRYPTO_EcdhePublicKey ephemeral_key;
+
+      GNUNET_CRYPTO_ecdhe_key_get_public (&ax_tmp.kx_0,
+                                          &ephemeral_key);
+      if (0 != memcmp (&ephemeral_key,
+                       &msg->r_ephemeral_key_XXX,
+                       sizeof (ephemeral_key)))
+      {
+        LOG (GNUNET_ERROR_TYPE_WARNING,
+           "My ephemeral is %s!\n",
+             GNUNET_e2s (&ephemeral_key));
+        LOG (GNUNET_ERROR_TYPE_WARNING,
+             "Response is for ephemeral %s!\n",
+             GNUNET_e2s (&msg->r_ephemeral_key_XXX));
+      }
+      else
+      {
+        test_crypto_bug (&ax_tmp.kx_0,
+                         &msg->kx.ephemeral_key_XXX,
+                         my_private_key,
+                         &msg->kx.private_key_XXX);
+      }
+    }
+#endif
+    if (NULL == t->kx_task)
+      t->kx_task
+        = GNUNET_SCHEDULER_add_at (t->next_kx_attempt,
+                                   &retry_kx,
+                                   t);
     return;
   }
   /* Yep, we're good. */
@@ -1862,6 +2048,13 @@ GCT_handle_kx_auth (struct CadetTConnection *ct,
        Nothing to do here. */
     break;
   }
+  if (0 != (GNUNET_CADET_KX_FLAG_FORCE_REPLY & ntohl (msg->kx.flags)))
+  {
+    send_kx_auth (t,
+                 NULL,
+                 &t->ax,
+                 GNUNET_NO);
+  }
 }
 
 
@@ -2236,8 +2429,8 @@ connection_ready_cb (void *cls,
   {
   case CADET_TUNNEL_KEY_UNINITIALIZED:
     /* Do not begin KX if WE have no channels waiting! */
-    if (0 == GCT_count_channels (t))
-      return;
+    if (0 != GNUNET_TIME_absolute_get_remaining (t->next_kx_attempt).rel_value_us)
+      return; /* wait for timeout before retrying */
     /* We are uninitialized, just transmit immediately,
        without undue delay. */
     if (NULL != t->kx_task)
@@ -2248,6 +2441,15 @@ connection_ready_cb (void *cls,
     send_kx (t,
              ct,
              &t->ax);
+    if ( (0 ==
+         GCT_count_channels (t)) &&
+        (NULL == t->destroy_task) )
+    {
+      t->destroy_task
+       = GNUNET_SCHEDULER_add_delayed (IDLE_DESTROY_DELAY,
+                                       &destroy_tunnel,
+                                       t);
+    }
     break;
   case CADET_TUNNEL_KEY_AX_RECV:
   case CADET_TUNNEL_KEY_AX_SENT:
@@ -2263,6 +2465,8 @@ connection_ready_cb (void *cls,
   case CADET_TUNNEL_KEY_OK:
     if (GNUNET_YES == t->kx_auth_requested)
     {
+      if (0 != GNUNET_TIME_absolute_get_remaining (t->next_kx_attempt).rel_value_us)
+        return; /* wait for timeout */
       if (NULL != t->kx_task)
       {
         GNUNET_SCHEDULER_cancel (t->kx_task);
@@ -2370,15 +2574,21 @@ evaluate_connection (void *cls,
 {
   struct EvaluationSummary *es = cls;
   struct CadetConnection *cc = ct->cc;
-  struct CadetPeerPath *ps = GCC_get_path (cc);
+  unsigned int ct_length;
+  struct CadetPeerPath *ps;
   const struct CadetConnectionMetrics *metrics;
   GNUNET_CONTAINER_HeapCostType ct_desirability;
   struct GNUNET_TIME_Relative uptime;
   struct GNUNET_TIME_Relative last_use;
-  uint32_t ct_length;
   double score;
   double success_rate;
 
+  ps = GCC_get_path (cc,
+                     &ct_length);
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Evaluating path %s of existing %s\n",
+       GCPP_2s (ps),
+       GCC_2s (cc));
   if (ps == es->path)
   {
     LOG (GNUNET_ERROR_TYPE_DEBUG,
@@ -2387,8 +2597,39 @@ evaluate_connection (void *cls,
     es->duplicate = GNUNET_YES;
     return;
   }
+  if (NULL != es->path)
+  {
+    int duplicate = GNUNET_YES;
+
+    for (unsigned int i=0;i<ct_length;i++)
+    {
+      GNUNET_assert (GCPP_get_length (es->path) > i);
+      if (GCPP_get_peer_at_offset (es->path,
+                                   i) !=
+          GCPP_get_peer_at_offset (ps,
+                                   i))
+      {
+        duplicate = GNUNET_NO;
+        break;
+      }
+    }
+    if (GNUNET_YES == duplicate)
+    {
+      LOG (GNUNET_ERROR_TYPE_DEBUG,
+           "Ignoring overlapping path %s.\n",
+           GCPP_2s (es->path));
+      es->duplicate = GNUNET_YES;
+      return;
+    }
+    else
+    {
+      LOG (GNUNET_ERROR_TYPE_DEBUG,
+           "Known path %s differs from proposed path\n",
+           GCPP_2s (ps));
+    }
+  }
+
   ct_desirability = GCPP_get_desirability (ps);
-  ct_length = GCPP_get_length (ps);
   metrics = GCC_get_metrics (cc);
   uptime = GNUNET_TIME_absolute_get_duration (metrics->age);
   last_use = GNUNET_TIME_absolute_get_duration (metrics->last_use);
@@ -2437,6 +2678,8 @@ consider_path_cb (void *cls,
   struct CadetTConnection *ct;
 
   GNUNET_assert (off < GCPP_get_length (path));
+  GNUNET_assert (GCPP_get_peer_at_offset (path,
+                                          off) == t->destination);
   es.min_length = UINT_MAX;
   es.max_length = 0;
   es.max_desire = 0;
@@ -2446,6 +2689,13 @@ consider_path_cb (void *cls,
   es.worst = NULL;
 
   /* Compute evaluation summary over existing connections. */
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Evaluating proposed path %s for target %s\n",
+       GCPP_2s (path),
+       GCT_2s (t));
+  /* FIXME: suspect this does not ACTUALLY iterate
+     over all existing paths, otherwise dup detection
+     should work!!! */
   GCT_iterate_connections (t,
                            &evaluate_connection,
                            &es);
@@ -2590,9 +2840,10 @@ GCT_consider_path (struct CadetTunnel *t,
                    unsigned int off)
 {
   LOG (GNUNET_ERROR_TYPE_DEBUG,
-       "Considering %s for %s\n",
+       "Considering %s for %s (offset %u)\n",
        GCPP_2s (p),
-       GCT_2s (t));
+       GCT_2s (t),
+       off);
   (void) consider_path_cb (t,
                            p,
                            off);
@@ -2856,7 +3107,9 @@ handle_plaintext_channel_destroy (void *cls,
  *
  * @param cls the `struct CadetTunnel` that got the message
  * @param msg the message
- * @return #GNUNET_OK (continue to process)
+ * @return #GNUNET_OK on success (always)
+ *    #GNUNET_NO to stop further processing (no error)
+ *    #GNUNET_SYSERR to stop further processing with error
  */
 static int
 handle_decrypted (void *cls,
@@ -3404,6 +3657,7 @@ void
 GCT_debug (const struct CadetTunnel *t,
            enum GNUNET_ErrorType level)
 {
+#if !defined(GNUNET_CULL_LOGGING)
   struct CadetTConnection *iter_c;
   int do_log;
 
@@ -3435,7 +3689,8 @@ GCT_debug (const struct CadetTunnel *t,
 
   LOG2 (level,
         "TTT TUNNEL END\n");
+#endif
 }
 
 
-/* end of gnunet-service-cadet-new_tunnels.c */
+/* end of gnunet-service-cadet_tunnels.c */