-more datacache integration work
[oweals/gnunet.git] / src / cadet / gnunet-service-cadet_tunnel.c
index 6a218ac097684db4730a2fd46e95e42505ea4f97..74926db1e0aa6c438d376587a31bbc91c8d522df 100644 (file)
@@ -188,8 +188,9 @@ struct CadetTunnelSkippedKey
   struct GNUNET_CRYPTO_SymmetricSessionKey MK;
 };
 
+
 /**
- * Axolotl data, according to @url https://github.com/trevp/axolotl/wiki .
+ * Axolotl data, according to https://github.com/trevp/axolotl/wiki .
  */
 struct CadetTunnelAxolotl
 {
@@ -281,9 +282,28 @@ struct CadetTunnelAxolotl
   uint32_t PNs;
 
   /**
-   * True (#GNUNET_YES) if the party will send a new ratchet key in next msg.
+   * True (#GNUNET_YES) if we have to send a new ratchet key in next msg.
    */
   int ratchet_flag;
+
+  /**
+   * Number of messages recieved since our last ratchet advance.
+   * - If this counter = 0, we cannot send a new ratchet key in next msg.
+   * - If this counter > 0, we can (but don't yet have to) send a new key.
+   */
+  unsigned int ratchet_allowed;
+
+  /**
+   * Number of messages recieved since our last ratchet advance.
+   * - If this counter = 0, we cannot send a new ratchet key in next msg.
+   * - If this counter > 0, we can (but don't yet have to) send a new key.
+   */
+  unsigned int ratchet_counter;
+
+  /**
+   * When does this ratchet expire and a new one is triggered.
+   */
+  struct GNUNET_TIME_Absolute ratchet_expiration;
 };
 
 /**
@@ -506,12 +526,12 @@ static struct GNUNET_CONTAINER_MultiPeerMap *tunnels;
  */
 static unsigned long long default_ttl;
 
-
 /**
  * Own Peer ID private key.
  */
 const static struct GNUNET_CRYPTO_EddsaPrivateKey *id_key;
 
+
 /********************************  AXOLOTL ************************************/
 
 static struct GNUNET_CRYPTO_EcdhePrivateKey *ax_key;
@@ -521,8 +541,18 @@ static struct GNUNET_CRYPTO_EcdhePrivateKey *ax_key;
  */
 static struct CadetAxolotlSignedKey ax_identity;
 
-/********************************    OTR   ***********************************/
+/**
+ * How many messages are needed to trigger a ratchet advance.
+ */
+static unsigned long long ratchet_messages;
 
+/**
+ * How long until we trigger a ratched advance.
+ */
+static struct GNUNET_TIME_Relative ratchet_time;
+
+
+/********************************    OTR   ***********************************/
 
 /**
  * Own global OTR ephemeral private key.
@@ -995,6 +1025,14 @@ t_ax_encrypt (struct CadetTunnel *t, void *dst, const void *src, size_t size)
 
   ax = t->ax;
 
+  ax->ratchet_counter++;
+  if (GNUNET_YES == ax->ratchet_allowed
+      && (ratchet_messages <= ax->ratchet_counter
+          || 0 == GNUNET_TIME_absolute_get_remaining (ax->ratchet_expiration).rel_value_us))
+  {
+    ax->ratchet_flag = GNUNET_YES;
+  }
+
   if (GNUNET_YES == ax->ratchet_flag)
   {
     /* Advance ratchet */
@@ -1018,6 +1056,10 @@ t_ax_encrypt (struct CadetTunnel *t, void *dst, const void *src, size_t size)
     ax->PNs = ax->Ns;
     ax->Ns = 0;
     ax->ratchet_flag = GNUNET_NO;
+    ax->ratchet_allowed = GNUNET_NO;
+    ax->ratchet_counter = 0;
+    ax->ratchet_expiration =
+      GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get(), ratchet_time);
   }
 
   t_hmac_derive_key (&ax->CKs, &MK, "0", 1);
@@ -1327,6 +1369,49 @@ try_old_ax_keys (struct CadetTunnel *t, struct GNUNET_CADET_AX *dst,
 }
 
 
+/**
+ * Delete a key from the list of skipped keys.
+ *
+ * @param t Tunnel to delete from.
+ * @param HKr Header Key to use.
+ */
+static void
+store_skipped_key (struct CadetTunnel *t,
+                   const struct GNUNET_CRYPTO_SymmetricSessionKey *HKr)
+{
+  struct CadetTunnelSkippedKey *key;
+
+  key = GNUNET_new (struct CadetTunnelSkippedKey);
+  key->timestamp = GNUNET_TIME_absolute_get ();
+  t_hmac_derive_key (&t->ax->CKr, &key->MK, "0", 1);
+  #if DUMP_KEYS_TO_STDERR
+  LOG (GNUNET_ERROR_TYPE_INFO, "    storing MK for Nr %u: %s\n",
+       t->ax->Nr, GNUNET_h2s ((struct GNUNET_HashCode *) &key->MK));
+  LOG (GNUNET_ERROR_TYPE_INFO, "    for CKr: %s\n",
+       GNUNET_h2s ((struct GNUNET_HashCode *) &t->ax->CKr));
+  #endif
+  t_hmac_derive_key (&t->ax->CKr, &t->ax->CKr, "1", 1);
+  GNUNET_CONTAINER_DLL_insert (t->ax->skipped_head, t->ax->skipped_tail, key);
+  t->ax->Nr++;
+  t->ax->skipped++;
+}
+
+
+/**
+ * Delete a key from the list of skipped keys.
+ *
+ * @param t Tunnel to delete from.
+ * @param key Key to delete.
+ */
+static void
+delete_skipped_key (struct CadetTunnel *t, struct CadetTunnelSkippedKey *key)
+{
+  GNUNET_CONTAINER_DLL_remove (t->ax->skipped_head, t->ax->skipped_tail, key);
+  GNUNET_free (key);
+  t->ax->skipped--;
+}
+
+
 /**
  * Stage skipped AX keys and calculate the message key.
  *
@@ -1334,21 +1419,16 @@ try_old_ax_keys (struct CadetTunnel *t, struct GNUNET_CADET_AX *dst,
  *
  * @param t Tunnel where to stage the keys.
  * @param HKr Header key.
- * @param Nr Current message number.
  * @param Np Received meesage number.
- * @param CKr[in/out] Chain key, gets ratcheted forward to the new state.
  */
 static void
 store_ax_keys (struct CadetTunnel *t,
                const struct GNUNET_CRYPTO_SymmetricSessionKey *HKr,
-               uint32_t Nr, uint32_t Np,
-               struct GNUNET_CRYPTO_SymmetricSessionKey *CKr)
+               uint32_t Np)
 {
-  struct CadetTunnelSkippedKey *key;
-  unsigned int i;
   int gap;
 
-  gap = Np - Nr;
+  gap = Np - t->ax->Nr;
   if (MAX_KEY_GAP < gap || 0 > gap)
   {
     /* Avoid DoS (forcing peer to do 2*33 chain HMAC operations) */
@@ -1357,21 +1437,11 @@ store_ax_keys (struct CadetTunnel *t,
     return;
   }
 
-  for (i = Nr; i < Np; i++)
-  {
-    key = GNUNET_new (struct CadetTunnelSkippedKey);
-    key->timestamp = GNUNET_TIME_absolute_get ();
-    t_hmac_derive_key (CKr, &key->MK, "0", 1);
-    #if DUMP_KEYS_TO_STDERR
-    LOG (GNUNET_ERROR_TYPE_INFO, "    storing MK for Nr %u: %s\n",
-         i, GNUNET_h2s ((struct GNUNET_HashCode *) &key->MK));
-    LOG (GNUNET_ERROR_TYPE_INFO, "    for CKr: %s\n",
-         GNUNET_h2s ((struct GNUNET_HashCode *) &t->ax->CKr));
-    #endif
-    t_hmac_derive_key (CKr, CKr, "1", 1);
-    GNUNET_CONTAINER_DLL_insert (t->ax->skipped_head, t->ax->skipped_tail, key);
-    t->ax->skipped++;
-  }
+  while (t->ax->Nr < Np)
+    store_skipped_key (t, HKr);
+
+  while (t->ax->skipped > MAX_SKIPPED_KEYS)
+    delete_skipped_key (t, t->ax->skipped_tail);
 }
 
 
@@ -1431,7 +1501,7 @@ t_ax_decrypt_and_validate (struct CadetTunnel *t, void *dst,
     Np = ntohl (dstmsg->Ns);
     PNp = ntohl (dstmsg->PNs);
     DHRp = &dstmsg->DHRs;
-    store_ax_keys (t, &HK, ax->Nr, PNp, &ax->CKr);
+    store_ax_keys (t, &HK, PNp);
 
     /* RKp, NHKp, CKp = KDF (HMAC-HASH (RK, DH (DHRp, DHRs))) */
     GNUNET_CRYPTO_ecc_ecdh (ax->DHRs, DHRp, &dh);
@@ -1445,6 +1515,7 @@ t_ax_decrypt_and_validate (struct CadetTunnel *t, void *dst,
     ax->CKr = keys[2];
     ax->DHRr = *DHRp;
     ax->Nr = 0;
+    ax->ratchet_allowed = GNUNET_YES;
   }
   else
   {
@@ -1455,7 +1526,7 @@ t_ax_decrypt_and_validate (struct CadetTunnel *t, void *dst,
   }
 
   if (Np > ax->Nr)
-    store_ax_keys (t, &ax->HKr, ax->Nr, Np, &ax->CKr);
+    store_ax_keys (t, &ax->HKr, Np);
 
   ax->Nr = Np + 1;
 
@@ -1754,11 +1825,7 @@ queue_data (struct CadetTunnel *t, const struct GNUNET_MessageHeader *msg)
 
   LOG (GNUNET_ERROR_TYPE_DEBUG, "queue data on Tunnel %s\n", GCT_2s (t));
 
-  if (GNUNET_YES == is_ready (t))
-  {
-    GNUNET_break (0);
-    return NULL;
-  }
+  GNUNET_assert (GNUNET_NO == is_ready (t));
 
   tqd = GNUNET_malloc (sizeof (struct CadetTunnelDelayed) + size);
 
@@ -1834,6 +1901,7 @@ send_prebuilt_message (const struct GNUNET_MessageHeader *message,
     msg = &ax_msg->header;
     msg->size = htons (sizeof (struct GNUNET_CADET_AX) + size);
     msg->type = htons (GNUNET_MESSAGE_TYPE_CADET_AX);
+    ax_msg->reserved = 0;
     esize = t_ax_encrypt (t, &ax_msg[1], message, size);
     ax_msg->Ns = htonl (t->ax->Ns++);
     ax_msg->PNs = htonl (t->ax->PNs);
@@ -1851,6 +1919,7 @@ send_prebuilt_message (const struct GNUNET_MessageHeader *message,
     t_hmac (&otr_msg[1], size, iv, select_key (t), &otr_msg->hmac);
     msg->size = htons (sizeof (struct GNUNET_CADET_Encrypted) + size);
     msg->type = htons (GNUNET_MESSAGE_TYPE_CADET_ENCRYPTED);
+    otr_msg->ttl = htonl (default_ttl);
   }
   GNUNET_assert (esize == size);
 
@@ -1963,6 +2032,29 @@ send_queued_data (struct CadetTunnel *t)
 }
 
 
+/**
+ * @brief Resend the AX KX until we complete the handshake.
+ *
+ * @param cls Closure (tunnel).
+ * @param tc Task context.
+ */
+static void
+ax_kx_resend (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct CadetTunnel *t = cls;
+
+  t->rekey_task = NULL;
+
+  if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+    return;
+
+  if (CADET_TUNNEL_KEY_OK == t->estate)
+    return;
+
+  GCT_send_ax_kx (t, GNUNET_YES);
+}
+
+
 /**
  * Callback called when a queued message is sent.
  *
@@ -1974,15 +2066,31 @@ send_queued_data (struct CadetTunnel *t)
  */
 static void
 ephm_sent (void *cls,
-         struct CadetConnection *c,
-         struct CadetConnectionQueue *q,
-         uint16_t type, int fwd, size_t size)
+           struct CadetConnection *c,
+           struct CadetConnectionQueue *q,
+           uint16_t type, int fwd, size_t size)
 {
   struct CadetTunnel *t = cls;
   LOG (GNUNET_ERROR_TYPE_DEBUG, "ephemeral sent %s\n", GC_m2s (type));
+
   t->ephm_h = NULL;
+
+  if (CADET_TUNNEL_KEY_OK == t->estate)
+    return;
+
+  if (CADET_Axolotl == t->enc_type && CADET_TUNNEL_KEY_OK != t->estate)
+  {
+    if (NULL != t->rekey_task)
+    {
+      GNUNET_break (0);
+      GNUNET_SCHEDULER_cancel (t->rekey_task);
+    }
+    t->rekey_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
+                                                  &ax_kx_resend, t);
+  }
 }
 
+
 /**
  * Callback called when a queued message is sent.
  *
@@ -2004,6 +2112,7 @@ pong_sent (void *cls,
   t->pong_h = NULL;
 }
 
+
 /**
  * Sends key exchange message on a tunnel, choosing the best connection.
  * Should not be called on loopback tunnels.
@@ -2619,6 +2728,17 @@ destroy_ax (struct CadetTunnel *t)
 
   GNUNET_free (t->ax);
   t->ax = NULL;
+
+  if (NULL != t->rekey_task)
+  {
+    GNUNET_SCHEDULER_cancel (t->rekey_task);
+    t->rekey_task = NULL;
+  }
+  if (NULL != t->ephm_h)
+  {
+    GCC_cancel (t->ephm_h);
+    t->ephm_h = NULL;
+  }
 }
 
 
@@ -2655,6 +2775,7 @@ handle_ephemeral (struct CadetTunnel *t,
       return;
     }
     rekey_tunnel (t, NULL);
+    GNUNET_STATISTICS_update (stats, "# otr-downgrades", -1, GNUNET_NO);
   }
 
   /**
@@ -2801,6 +2922,12 @@ handle_kx_ax (struct CadetTunnel *t, const struct GNUNET_CADET_AX_KX *msg)
     return;
   }
 
+  if (GNUNET_YES == ntohl (msg->force_reply))
+    GCT_send_ax_kx (t, GNUNET_NO);
+
+  if (CADET_TUNNEL_KEY_OK == t->estate)
+    return;
+
   LOG (GNUNET_ERROR_TYPE_INFO, " is Alice? %s\n", am_I_alice ? "YES" : "NO");
 
   ax = t->ax;
@@ -2868,6 +2995,10 @@ handle_kx_ax (struct CadetTunnel *t, const struct GNUNET_CADET_AX_KX *msg)
     ax->NHKs = keys[3];
     ax->CKs = keys[4];
     ax->ratchet_flag = GNUNET_NO;
+    ax->ratchet_allowed = GNUNET_NO;
+    ax->ratchet_counter = 0;
+    ax->ratchet_expiration =
+      GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get(), ratchet_time);
   }
   GCT_change_estate (t, CADET_TUNNEL_KEY_OK);
 }
@@ -3057,7 +3188,7 @@ GCT_init (const struct GNUNET_CONFIGURATION_Handle *c,
       GNUNET_CONFIGURATION_get_value_number (c, "CADET", "DEFAULT_TTL",
                                              &default_ttl))
   {
-    GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
+    GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_DEBUG,
                                "CADET", "DEFAULT_TTL", "USING DEFAULT");
     default_ttl = 64;
   }
@@ -3067,6 +3198,23 @@ GCT_init (const struct GNUNET_CONFIGURATION_Handle *c,
   {
     rekey_period = GNUNET_TIME_UNIT_DAYS;
   }
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_number (c, "CADET", "RATCHET_MESSAGES",
+                                             &ratchet_messages))
+  {
+    GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
+                               "CADET", "RATCHET_MESSAGES", "USING DEFAULT");
+    ratchet_messages = 64;
+  }
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_time (c, "CADET", "RATCHET_TIME",
+                                           &ratchet_time))
+  {
+    GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
+                               "CADET", "RATCHET_TIME", "USING DEFAULT");
+    ratchet_time = GNUNET_TIME_UNIT_HOURS;
+  }
+
 
   id_key = key;
 
@@ -3162,7 +3310,7 @@ GCT_change_cstate (struct CadetTunnel* t, enum CadetTunnelCState cstate)
     else if (CADET_TUNNEL_KEY_UNINITIALIZED == t->estate)
     {
       LOG (GNUNET_ERROR_TYPE_DEBUG, "  cstate triggered kx\n");
-      GCT_send_ax_kx (t);
+      GCT_send_ax_kx (t, GNUNET_NO);
     }
     else
     {
@@ -4060,16 +4208,23 @@ GCT_send_prebuilt_message (const struct GNUNET_MessageHeader *message,
  * Send an Axolotl KX message.
  *
  * @param t Tunnel on which to send it.
+ * @param force_reply Force the other peer to reply with a KX message.
  */
 void
-GCT_send_ax_kx (struct CadetTunnel *t)
+GCT_send_ax_kx (struct CadetTunnel *t, int force_reply)
 {
   struct GNUNET_CADET_AX_KX msg;
 
   LOG (GNUNET_ERROR_TYPE_INFO, "===> AX_KX for %s\n", GCT_2s (t));
+  if (NULL != t->ephm_h)
+  {
+    LOG (GNUNET_ERROR_TYPE_INFO, "     already queued\n");
+    return;
+  }
 
   msg.header.size = htons (sizeof (msg));
   msg.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_AX_KX);
+  msg.force_reply = htonl (force_reply);
   msg.permanent_key = ax_identity.permanent_key;
   msg.purpose = ax_identity.purpose;
   msg.signature = ax_identity.signature;
@@ -4077,7 +4232,8 @@ GCT_send_ax_kx (struct CadetTunnel *t)
   GNUNET_CRYPTO_ecdhe_key_get_public (t->ax->DHRs, &msg.ratchet_key);
 
   t->ephm_h = send_kx (t, &msg.header);
-  GCT_change_estate (t, CADET_TUNNEL_KEY_SENT);
+  if (CADET_TUNNEL_KEY_OK != t->estate)
+    GCT_change_estate (t, CADET_TUNNEL_KEY_SENT);
 }