how did this test ever work without assigning core handles?
[oweals/gnunet.git] / src / core / gnunet-service-core_kx.c
index f7fbfcf68ab79720a85b163d0a193ad44e09c785..9f904742dac12052952f8bf714f3548af7817ddd 100644 (file)
@@ -1,6 +1,6 @@
 /*
      This file is part of GNUnet.
-     (C) 2009, 2010, 2011, 2012 Christian Grothoff (and other contributing authors)
+     (C) 2009-2013 Christian Grothoff (and other contributing authors)
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
@@ -111,12 +111,12 @@ struct EphemeralKeyMessage
    * Ephemeral public ECC key (always for NIST P-521) encoded in a format suitable
    * for network transmission as created using 'gcry_sexp_sprint'.
    */
-  struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded ephemeral_key;  
+  struct GNUNET_CRYPTO_EccPublicKey ephemeral_key;  
 
   /**
    * Public key of the signing peer (persistent version, not the ephemeral public key).
    */
-  struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded origin_public_key;
+  struct GNUNET_CRYPTO_EccPublicKey origin_public_key;
 
 };
 
@@ -270,8 +270,11 @@ enum KxStateMachine
   KX_STATE_UP,
 
   /**
-   * We're rekeying, so we have sent the other peer our new ephemeral
-   * key, but we did not get a matching PONG yet.
+   * We're rekeying (or had a timeout), so we have sent the other peer
+   * our new ephemeral key, but we did not get a matching PONG yet.
+   * This is equivalent to being 'KX_STATE_KEY_RECEIVED', except that
+   * the session is marked as 'up' with sessions (as we don't want to
+   * drop and re-establish P2P connections simply due to rekeying).
    */
   KX_STATE_REKEY_SENT
 
@@ -389,7 +392,7 @@ static struct EphemeralKeyMessage current_ekm;
 /**
  * Our public key.
  */
-static struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded my_public_key;
+static struct GNUNET_CRYPTO_EccPublicKey my_public_key;
 
 /**
  * Our message stream tokenizer (for encrypted payload).
@@ -495,7 +498,7 @@ derive_aes_key (const struct GNUNET_PeerIdentity *sender,
 
   GNUNET_CRYPTO_kdf (skey, sizeof (struct GNUNET_CRYPTO_AesSessionKey),
                     ctx, sizeof (ctx),
-                    skey, sizeof (struct GNUNET_CRYPTO_AesSessionKey),
+                    key_material, sizeof (struct GNUNET_HashCode),
                     sender, sizeof (struct GNUNET_PeerIdentity),
                     receiver, sizeof (struct GNUNET_PeerIdentity),
                     NULL);
@@ -615,6 +618,7 @@ set_key_retry_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 
   kx->retry_set_key_task = GNUNET_SCHEDULER_NO_TASK;
   kx->set_key_retry_frequency = GNUNET_TIME_STD_BACKOFF (kx->set_key_retry_frequency);
+  GNUNET_assert (KX_STATE_DOWN != kx->status);
   send_key (kx);
 }
 
@@ -668,8 +672,15 @@ GSC_KX_start (const struct GNUNET_PeerIdentity *pid)
   GNUNET_CONTAINER_DLL_insert (kx_head,
                               kx_tail,
                               kx);
-  kx->status = KX_STATE_KEY_SENT;
-  send_key (kx);
+  if (0 < GNUNET_CRYPTO_hash_cmp (&pid->hashPubKey,
+                                 &GSC_my_identity.hashPubKey))
+  {
+    /* peer with "lower" identity starts KX, otherwise we typically end up
+       with both peers starting the exchange and transmit the 'set key' 
+       message twice */
+    kx->status = KX_STATE_KEY_SENT;
+    send_key (kx);
+  }
   return kx;
 }
 
@@ -682,6 +693,7 @@ GSC_KX_start (const struct GNUNET_PeerIdentity *pid)
 void
 GSC_KX_stop (struct GSC_KeyExchangeInfo *kx)
 {
+  GSC_SESSIONS_end (&kx->peer);
   GNUNET_STATISTICS_update (GSC_stats, gettext_noop ("# key exchanges stopped"),
                             1, GNUNET_NO);
   if (kx->retry_set_key_task != GNUNET_SCHEDULER_NO_TASK)
@@ -740,6 +752,18 @@ GSC_KX_handle_ephemeral_key (struct GSC_KeyExchangeInfo *kx,
     return;
   }
   m = (const struct EphemeralKeyMessage *) msg;
+  end_t = GNUNET_TIME_absolute_ntoh (m->expiration_time);
+  if ( ( (KX_STATE_KEY_RECEIVED == kx->status) ||
+        (KX_STATE_UP == kx->status) ||
+        (KX_STATE_REKEY_SENT == kx->status) ) &&       
+       (end_t.abs_value_us <= kx->foreign_key_expires.abs_value_us) )
+  {
+    GNUNET_STATISTICS_update (GSC_stats, gettext_noop ("# old ephemeral keys ignored"),
+                             1, GNUNET_NO);
+    return;
+  }
+  start_t = GNUNET_TIME_absolute_ntoh (m->creation_time);
+
   GNUNET_STATISTICS_update (GSC_stats, gettext_noop ("# ephemeral keys received"),
                             1, GNUNET_NO);
 
@@ -747,7 +771,7 @@ GSC_KX_handle_ephemeral_key (struct GSC_KeyExchangeInfo *kx,
               "Core service receives `%s' request from `%4s'.\n", "EPHEMERAL_KEY",
               GNUNET_i2s (&kx->peer));
   GNUNET_CRYPTO_hash (&m->origin_public_key,
-                     sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded),
+                     sizeof (struct GNUNET_CRYPTO_EccPublicKey),
                      &signer_id.hashPubKey);
   if (0 !=
       memcmp (&signer_id, &kx->peer,
@@ -760,8 +784,8 @@ GSC_KX_handle_ephemeral_key (struct GSC_KeyExchangeInfo *kx,
        sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
        sizeof (struct GNUNET_TIME_AbsoluteNBO) +
        sizeof (struct GNUNET_TIME_AbsoluteNBO) +
-       sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded) +
-       sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded)) ||
+       sizeof (struct GNUNET_CRYPTO_EccPublicKey) +
+       sizeof (struct GNUNET_CRYPTO_EccPublicKey)) ||
       (GNUNET_OK !=
        GNUNET_CRYPTO_ecc_verify (GNUNET_SIGNATURE_PURPOSE_SET_ECC_KEY,
                                 &m->purpose,
@@ -771,17 +795,16 @@ GSC_KX_handle_ephemeral_key (struct GSC_KeyExchangeInfo *kx,
     GNUNET_break_op (0);
     return;
   }
-  start_t = GNUNET_TIME_absolute_ntoh (m->creation_time);
-  end_t = GNUNET_TIME_absolute_ntoh (m->expiration_time);
   now = GNUNET_TIME_absolute_get ();
-  if ( (end_t.abs_value < GNUNET_TIME_absolute_subtract (now, REKEY_TOLERANCE).abs_value) ||
-       (start_t.abs_value > GNUNET_TIME_absolute_add (now, REKEY_TOLERANCE).abs_value) )
+  if ( (end_t.abs_value_us < GNUNET_TIME_absolute_subtract (now, REKEY_TOLERANCE).abs_value_us) ||
+       (start_t.abs_value_us > GNUNET_TIME_absolute_add (now, REKEY_TOLERANCE).abs_value_us) )
   {
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-               _("Ephemeral key message rejected as its validity range does not match our system time (%llu not in [%llu,%llu]).\n"),
-               now.abs_value,
-               start_t.abs_value,
-               end_t.abs_value);
+               _("Ephemeral key message from peer `%s' rejected as its validity range does not match our system time (%llu not in [%llu,%llu]).\n"),
+               GNUNET_i2s (&kx->peer),
+               now.abs_value_us,
+               start_t.abs_value_us,
+               end_t.abs_value_us);
     return;
   }
   if (GNUNET_OK !=
@@ -814,11 +837,10 @@ GSC_KX_handle_ephemeral_key (struct GSC_KeyExchangeInfo *kx,
   switch (sender_status)
   {
   case KX_STATE_DOWN:
-    /* makes no sense, should be at least KX_STATE_KEY_SENT */
     GNUNET_break_op (0);
     break;
   case KX_STATE_KEY_SENT:
-    send_key (kx);
+    /* fine, need to send our key after updating our status, see below */
     break;
   case KX_STATE_KEY_RECEIVED:
   case KX_STATE_UP: 
@@ -833,23 +855,35 @@ GSC_KX_handle_ephemeral_key (struct GSC_KeyExchangeInfo *kx,
   switch (kx->status)
   {
   case KX_STATE_DOWN:
+    GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == kx->keep_alive_task);
     kx->status = KX_STATE_KEY_RECEIVED;
+    if (KX_STATE_KEY_SENT == sender_status)
+      send_key (kx);
     send_ping (kx);
     break;
   case KX_STATE_KEY_SENT:
+    GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == kx->keep_alive_task);
     kx->status = KX_STATE_KEY_RECEIVED;
+    if (KX_STATE_KEY_SENT == sender_status)
+      send_key (kx);
     send_ping (kx);
     break;
   case KX_STATE_KEY_RECEIVED:
+    GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == kx->keep_alive_task);
+    if (KX_STATE_KEY_SENT == sender_status)
+      send_key (kx);
     send_ping (kx);
     break;
   case KX_STATE_UP: 
-    kx->status = KX_STATE_KEY_RECEIVED;
+    kx->status = KX_STATE_REKEY_SENT;
+    if (KX_STATE_KEY_SENT == sender_status)
+      send_key (kx);
     /* we got a new key, need to reconfirm! */
     send_ping (kx);
     break;
   case KX_STATE_REKEY_SENT:
-    kx->status = KX_STATE_KEY_RECEIVED;
+    if (KX_STATE_KEY_SENT == sender_status)
+      send_key (kx);
     /* we got a new key, need to reconfirm! */
     send_ping (kx);
     break;
@@ -960,7 +994,7 @@ send_keep_alive (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 
   kx->keep_alive_task = GNUNET_SCHEDULER_NO_TASK;
   left = GNUNET_TIME_absolute_get_remaining (kx->timeout);
-  if (0 == left.rel_value)
+  if (0 == left.rel_value_us)
   {
     GNUNET_STATISTICS_update (GSC_stats,
                               gettext_noop ("# sessions terminated by timeout"),
@@ -1142,6 +1176,7 @@ GSC_KX_handle_pong (struct GSC_KeyExchangeInfo *kx,
 static void
 send_key (struct GSC_KeyExchangeInfo *kx)
 {
+  GNUNET_assert (KX_STATE_DOWN != kx->status);
   if (GNUNET_SCHEDULER_NO_TASK != kx->retry_set_key_task)
   {
      GNUNET_SCHEDULER_cancel (kx->retry_set_key_task);
@@ -1217,19 +1252,14 @@ struct DeliverMessageContext
 {
 
   /**
-   * Performance information for the connection.
+   * Key exchange context.
    */
-  const struct GNUNET_ATS_Information *atsi;
+  struct GSC_KeyExchangeInfo *kx;
 
   /**
    * Sender of the message.
    */
   const struct GNUNET_PeerIdentity *peer;
-
-  /**
-   * Number of entries in 'atsi' array.
-   */
-  uint32_t atsi_count;
 };
 
 
@@ -1239,14 +1269,10 @@ struct DeliverMessageContext
  *
  * @param kx key exchange context for encrypting the message
  * @param msg encrypted message
- * @param atsi performance data
- * @param atsi_count number of entries in ats (excluding 0-termination)
  */
 void
 GSC_KX_handle_encrypted_message (struct GSC_KeyExchangeInfo *kx,
-                                 const struct GNUNET_MessageHeader *msg,
-                                 const struct GNUNET_ATS_Information *atsi,
-                                 uint32_t atsi_count)
+                                 const struct GNUNET_MessageHeader *msg)
 {
   const struct EncryptedMessage *m;
   struct EncryptedMessage *pt;  /* plaintext */
@@ -1274,13 +1300,21 @@ GSC_KX_handle_encrypted_message (struct GSC_KeyExchangeInfo *kx,
                               1, GNUNET_NO);
     return;
   }
-  if (0 == GNUNET_TIME_absolute_get_remaining (kx->foreign_key_expires).rel_value)
+  if (0 == GNUNET_TIME_absolute_get_remaining (kx->foreign_key_expires).rel_value_us)
   {
-    kx->status = KX_STATE_KEY_SENT;
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+               _("Session to peer `%s' went down due to key expiration (should not happen)\n"),
+               GNUNET_i2s (&kx->peer));
     GNUNET_STATISTICS_update (GSC_stats,
-                              gettext_noop
-                              ("# DATA message dropped (session key expired)"),
+                              gettext_noop ("# sessions terminated by key expiration"),
                               1, GNUNET_NO);
+    GSC_SESSIONS_end (&kx->peer);
+    if (GNUNET_SCHEDULER_NO_TASK != kx->keep_alive_task)
+    {
+      GNUNET_SCHEDULER_cancel (kx->keep_alive_task);
+      kx->keep_alive_task = GNUNET_SCHEDULER_NO_TASK;
+    }
+    kx->status = KX_STATE_KEY_SENT;
     send_key (kx);
     return;
   }
@@ -1360,8 +1394,8 @@ GSC_KX_handle_encrypted_message (struct GSC_KeyExchangeInfo *kx,
 
   /* check timestamp */
   t = GNUNET_TIME_absolute_ntoh (pt->timestamp);
-  if (GNUNET_TIME_absolute_get_duration (t).rel_value >
-      MAX_MESSAGE_AGE.rel_value)
+  if (GNUNET_TIME_absolute_get_duration (t).rel_value_us >
+      MAX_MESSAGE_AGE.rel_value_us)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                 "Message received far too old (%s). Content ignored.\n",
@@ -1378,8 +1412,7 @@ GSC_KX_handle_encrypted_message (struct GSC_KeyExchangeInfo *kx,
   GNUNET_STATISTICS_update (GSC_stats,
                             gettext_noop ("# bytes of payload decrypted"),
                             size - sizeof (struct EncryptedMessage), GNUNET_NO);
-  dmc.atsi = atsi;
-  dmc.atsi_count = atsi_count;
+  dmc.kx = kx;
   dmc.peer = &kx->peer;
   if (GNUNET_OK !=
       GNUNET_SERVER_mst_receive (mst, &dmc,
@@ -1404,6 +1437,14 @@ deliver_message (void *cls, void *client, const struct GNUNET_MessageHeader *m)
 {
   struct DeliverMessageContext *dmc = client;
 
+  if (KX_STATE_UP != dmc->kx->status)
+  {
+    GNUNET_STATISTICS_update (GSC_stats,
+                              gettext_noop
+                              ("# PAYLOAD dropped (out of order)"),
+                              1, GNUNET_NO);
+    return GNUNET_OK;
+  }
   switch (ntohs (m->type))
   {
   case GNUNET_MESSAGE_TYPE_CORE_BINARY_TYPE_MAP:
@@ -1411,10 +1452,10 @@ deliver_message (void *cls, void *client, const struct GNUNET_MessageHeader *m)
     GSC_SESSIONS_set_typemap (dmc->peer, m);
     return GNUNET_OK;
   default:
-    GSC_CLIENTS_deliver_message (dmc->peer, dmc->atsi, dmc->atsi_count, m,
+    GSC_CLIENTS_deliver_message (dmc->peer, m,
                                  ntohs (m->size),
                                  GNUNET_CORE_OPTION_SEND_FULL_INBOUND);
-    GSC_CLIENTS_deliver_message (dmc->peer, dmc->atsi, dmc->atsi_count, m,
+    GSC_CLIENTS_deliver_message (dmc->peer, m,
                                  sizeof (struct GNUNET_MessageHeader),
                                  GNUNET_CORE_OPTION_SEND_HDR_INBOUND);
   }
@@ -1436,11 +1477,21 @@ sign_ephemeral_key ()
   current_ekm.purpose.size = htonl (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
                                    sizeof (struct GNUNET_TIME_AbsoluteNBO) +
                                    sizeof (struct GNUNET_TIME_AbsoluteNBO) +
-                                   sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded) +
-                                   sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded));
+                                   sizeof (struct GNUNET_CRYPTO_EccPublicKey) +
+                                   sizeof (struct GNUNET_CRYPTO_EccPublicKey));
   current_ekm.creation_time = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
-  current_ekm.expiration_time = GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_add (REKEY_FREQUENCY,
-                                                                                                                      REKEY_TOLERANCE)));
+  if (GNUNET_YES ==
+      GNUNET_CONFIGURATION_get_value_yesno (GSC_cfg,
+                                           "core",
+                                           "USE_EPHEMERAL_KEYS"))
+  {
+    current_ekm.expiration_time = GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_add (REKEY_FREQUENCY,
+                                                                                                                        REKEY_TOLERANCE)));
+  }
+  else
+  {
+    current_ekm.expiration_time = GNUNET_TIME_absolute_hton (GNUNET_TIME_UNIT_FOREVER_ABS);
+  }
   GNUNET_CRYPTO_ecc_key_get_public (my_ephemeral_key,
                                    &current_ekm.ephemeral_key);
   current_ekm.origin_public_key = my_public_key;
@@ -1467,7 +1518,7 @@ do_rekey (void *cls,
                                             &do_rekey,
                                             NULL);
   if (NULL != my_ephemeral_key)
-    GNUNET_CRYPTO_ecc_key_free (my_ephemeral_key);
+    GNUNET_free (my_ephemeral_key);
   my_ephemeral_key = GNUNET_CRYPTO_ecc_key_create ();
   GNUNET_assert (NULL != my_ephemeral_key);
   sign_ephemeral_key ();
@@ -1493,19 +1544,30 @@ GSC_KX_init (struct GNUNET_CRYPTO_EccPrivateKey *pk)
   GNUNET_CRYPTO_ecc_key_get_public (my_private_key, &my_public_key);
   GNUNET_CRYPTO_hash (&my_public_key, sizeof (my_public_key),
                       &GSC_my_identity.hashPubKey);
-  my_ephemeral_key = GNUNET_CRYPTO_ecc_key_create ();
-  if (NULL == my_ephemeral_key)
+  if (GNUNET_YES ==
+      GNUNET_CONFIGURATION_get_value_yesno (GSC_cfg,
+                                           "core",
+                                           "USE_EPHEMERAL_KEYS"))
   {
-    GNUNET_break (0);
-    GNUNET_CRYPTO_ecc_key_free (my_private_key);
-    my_private_key = NULL;
-    return GNUNET_SYSERR;
+    my_ephemeral_key = GNUNET_CRYPTO_ecc_key_create ();
+    if (NULL == my_ephemeral_key)
+    {
+      GNUNET_break (0);
+      GNUNET_free (my_private_key);
+      my_private_key = NULL;
+      return GNUNET_SYSERR;
+    }
+    sign_ephemeral_key ();
+    rekey_task = GNUNET_SCHEDULER_add_delayed (REKEY_FREQUENCY,
+                                              &do_rekey,
+                                              NULL);
+  }
+  else
+  {
+    my_ephemeral_key = my_private_key;
+    sign_ephemeral_key ();
   }
-  sign_ephemeral_key ();
   mst = GNUNET_SERVER_mst_create (&deliver_message, NULL);
-  rekey_task = GNUNET_SCHEDULER_add_delayed (REKEY_FREQUENCY,
-                                            &do_rekey,
-                                            NULL);
   return GNUNET_OK;
 }
 
@@ -1521,15 +1583,16 @@ GSC_KX_done ()
     GNUNET_SCHEDULER_cancel (rekey_task);
     rekey_task = GNUNET_SCHEDULER_NO_TASK;
   }
-  if (NULL != my_private_key)
+  if ( (NULL != my_ephemeral_key) &&
+       (my_ephemeral_key != my_private_key) )
   {
-    GNUNET_CRYPTO_ecc_key_free (my_private_key);
-    my_private_key = NULL;
+    GNUNET_free (my_ephemeral_key);
+    my_ephemeral_key = NULL;
   }
-  if (NULL != my_ephemeral_key)
+  if (NULL != my_private_key)
   {
-    GNUNET_CRYPTO_ecc_key_free (my_ephemeral_key);
-    my_ephemeral_key = NULL;
+    GNUNET_free (my_private_key);
+    my_private_key = NULL;
   }
   if (NULL != mst)
   {