View Issue Details

IDProjectCategoryView StatusLast Update
0002563GNUnetcore servicepublic2013-12-24 20:55
ReporterChristian Grothoff Assigned ToChristian Grothoff  
PriorityurgentSeverityfeatureReproducibilityN/A
Status closedResolutionfixed 
Product VersionGit master 
Target Version0.10.0Fixed in Version0.10.0 
Summary0002563: core should use DH for PFS
DescriptionWe should switch from RSA to DH to eventually get perfect forward secrecy for all of our traffic. (request from LynX).
TagsNo tags attached.
Attached Files
combined.diff (95,019 bytes)   
Index: README.1st
===================================================================
--- README.1st	(revision 0)
+++ README.1st	(working copy)
@@ -0,0 +1,12 @@
+======================================================================
+
+THIS SVN VERSION OF GNUNET IS INCOMPATIBLE TO ALL PREVIOUS RELEASES.
+WE ARE WORKING ON CHANGING FUNDAMENTAL PARTS OF THE CRYPTOGRAPHIC
+PARTS OF THE SYSTEM.  
+
+THE CURRENT IMPLEMENTATION DOES NOT GIVE YOU ANY SECURITY (AS SOME
+CRYPTO IS DISABLED/INCOMPLETE) AND WILL LIKELY NOT BE COMPATIBLE WITH
+YESTERDAYS OR TOMORROWS VERSION.  AS SUCH, DO NOT USE IT, OTHER THAN
+FOR DEVELOPMENT!
+
+=======================================================================
Index: NEWS
===================================================================
--- NEWS	(revision 26008)
+++ NEWS	(working copy)
@@ -1 +1,12 @@
-See ChangeLog.
+======================================================================
+
+THIS SVN VERSION OF GNUNET IS INCOMPATIBLE TO ALL PREVIOUS RELEASES.
+WE ARE WORKING ON CHANGING FUNDAMENTAL PARTS OF THE CRYPTOGRAPHIC
+PARTS OF THE SYSTEM.  
+
+THE CURRENT IMPLEMENTATION DOES NOT GIVE YOU ANY SECURITY (AS SOME
+CRYPTO IS DISABLED/INCOMPLETE) AND WILL LIKELY NOT BE COMPATIBLE WITH
+YESTERDAYS OR TOMORROWS VERSION.  AS SUCH, DO NOT USE IT, OTHER THAN
+FOR DEVELOPMENT!
+
+=======================================================================
Index: src/testing/gnunet-testing.c
===================================================================
--- src/testing/gnunet-testing.c	(revision 26008)
+++ src/testing/gnunet-testing.c	(working copy)
@@ -28,7 +28,7 @@
 #include "gnunet_testing_lib.h"
 
 
-#define HOSTKEYFILESIZE 914
+#define HOSTKEYFILESIZE 1024
 
 /**
  * Final status code.
@@ -119,11 +119,13 @@
 static int
 create_hostkeys (const unsigned int no)
 {
+  static char pad[HOSTKEYFILESIZE];
   struct GNUNET_TESTING_System *system;
   struct GNUNET_PeerIdentity id;
   struct GNUNET_DISK_FileHandle *fd;
-  struct GNUNET_CRYPTO_RsaPrivateKey *pk;
-  struct GNUNET_CRYPTO_RsaPrivateKeyBinaryEncoded *pkb;
+  struct GNUNET_CRYPTO_EccPrivateKey *pk;
+  struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded *pkb;
+  ssize_t ret;
 
   system = GNUNET_TESTING_system_create ("testing", NULL, NULL);
   pk = GNUNET_TESTING_hostkey_get (system, create_no, &id);
@@ -140,14 +142,20 @@
 			      GNUNET_DISK_PERM_USER_READ |
 			      GNUNET_DISK_PERM_USER_WRITE);
   GNUNET_assert (fd != NULL);
-  pkb = GNUNET_CRYPTO_rsa_encode_key (pk);
-  GNUNET_assert (HOSTKEYFILESIZE ==
-		 GNUNET_DISK_file_write (fd, pkb, ntohs (pkb->len)));
+  pkb = GNUNET_CRYPTO_ecc_encode_key (pk);
+  ret = GNUNET_DISK_file_write (fd, pkb, 
+				ntohs (pkb->size));
+  GNUNET_assert (ntohs (pkb->size) == ret);
+  GNUNET_assert (ntohs (pkb->size) < HOSTKEYFILESIZE);
+  GNUNET_assert (HOSTKEYFILESIZE - ret ==
+		 GNUNET_DISK_file_write (fd, pad, 
+					 HOSTKEYFILESIZE - ret));
+  
   GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fd));
   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-testing",
 		   "Wrote hostkey to file: `%s'\n", create_hostkey);
   GNUNET_free (pkb);
-  GNUNET_CRYPTO_rsa_key_free (pk);
+  GNUNET_CRYPTO_ecc_key_free (pk);
   GNUNET_TESTING_system_destroy (system, GNUNET_YES);
   return 0;
 }
Index: src/testing/testing.c
===================================================================
--- src/testing/testing.c	(revision 26008)
+++ src/testing/testing.c	(working copy)
@@ -202,7 +202,7 @@
   
   GNUNET_assert (NULL == system->hostkeys_data);
   data_dir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
-  GNUNET_asprintf (&filename, "%s/testing_hostkeys.dat", data_dir);
+  GNUNET_asprintf (&filename, "%s/testing_hostkeys.ecc", data_dir);
   GNUNET_free (data_dir);  
 
   if (GNUNET_YES != GNUNET_DISK_file_test (filename))
@@ -525,13 +525,13 @@
  *        key; if NULL, GNUNET_SYSERR is returned immediately
  * @return NULL on error (not enough keys)
  */
-struct GNUNET_CRYPTO_RsaPrivateKey *
+struct GNUNET_CRYPTO_EccPrivateKey *
 GNUNET_TESTING_hostkey_get (const struct GNUNET_TESTING_System *system,
 			    uint32_t key_number,
 			    struct GNUNET_PeerIdentity *id)
 {  
-  struct GNUNET_CRYPTO_RsaPrivateKey *private_key;
-  struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
+  struct GNUNET_CRYPTO_EccPrivateKey *private_key;
+  struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded public_key;
   
   if ((NULL == id) || (NULL == system->hostkeys_data))
     return NULL;
@@ -541,7 +541,7 @@
          _("Key number %u does not exist\n"), key_number);
     return NULL;
   }   
-  private_key = GNUNET_CRYPTO_rsa_decode_key (system->hostkeys_data +
+  private_key = GNUNET_CRYPTO_ecc_decode_key (system->hostkeys_data +
                                               (key_number *
                                                GNUNET_TESTING_HOSTKEYFILESIZE),
                                               GNUNET_TESTING_HOSTKEYFILESIZE);
@@ -551,9 +551,9 @@
          _("Error while decoding key %u\n"), key_number);
     return NULL;
   }
-  GNUNET_CRYPTO_rsa_key_get_public (private_key, &public_key);
+  GNUNET_CRYPTO_ecc_key_get_public (private_key, &public_key);
   GNUNET_CRYPTO_hash (&public_key,
-                      sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
+                      sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded),
                       &(id->hashPubKey));
   return private_key;
 }
@@ -855,7 +855,7 @@
   char *config_filename;
   char *libexec_binary;
   char *emsg_;
-  struct GNUNET_CRYPTO_RsaPrivateKey *pk;
+  struct GNUNET_CRYPTO_EccPrivateKey *pk;
 
   if (NULL != emsg)
     *emsg = NULL;
@@ -897,12 +897,13 @@
     return NULL;
   }
   if (NULL != pk)
-    GNUNET_CRYPTO_rsa_key_free (pk);
+    GNUNET_CRYPTO_ecc_key_free (pk);
   GNUNET_assert (GNUNET_OK == 
                  GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS",
                                                         "SERVICEHOME",
                                                         &service_home));
-  GNUNET_snprintf (hostkey_filename, sizeof (hostkey_filename), "%s/.hostkey",
+  /* FIXME: might be better to evaluate actual configuration option here... */
+  GNUNET_snprintf (hostkey_filename, sizeof (hostkey_filename), "%s/private.ecc",
                    service_home);
   GNUNET_free (service_home);
   fd = GNUNET_DISK_file_open (hostkey_filename,
@@ -978,7 +979,7 @@
 GNUNET_TESTING_peer_get_identity (const struct GNUNET_TESTING_Peer *peer,
 				  struct GNUNET_PeerIdentity *id)
 {
-  GNUNET_CRYPTO_rsa_key_free (GNUNET_TESTING_hostkey_get (peer->system,
+  GNUNET_CRYPTO_ecc_key_free (GNUNET_TESTING_hostkey_get (peer->system,
 							  peer->key_number,
 							  id));
 }
Index: src/util/gnunet-ecc.c
===================================================================
--- src/util/gnunet-ecc.c	(revision 26008)
+++ src/util/gnunet-ecc.c	(working copy)
@@ -65,38 +65,12 @@
 
 
 /**
- * Create a new private key. Caller must free return value.
- *
- * @return fresh private key
- */
-static struct GNUNET_CRYPTO_EccPrivateKey *
-ecc_key_create ()
-{
-  struct GNUNET_CRYPTO_EccPrivateKey *ret;
-  gcry_sexp_t s_key;
-  gcry_sexp_t s_keyparam;
-
-  GNUNET_assert (0 ==
-                 gcry_sexp_build (&s_keyparam, NULL,
-                                  "(genkey(ecc(nbits %d)(ecc-use-e 3:257)))",
-                                  2048));
-  GNUNET_assert (0 == gcry_pk_genkey (&s_key, s_keyparam));
-  gcry_sexp_release (s_keyparam);
-#if EXTRA_CHECKS
-  GNUNET_assert (0 == gcry_pk_testkey (s_key));
-#endif
-  ret = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EccPrivateKey));
-  ret->sexp = s_key;
-  return ret;
-}
-
-
-/**
  * Create a flat file with a large number of key pairs for testing.
  */
 static void
 create_keys (const char *fn)
 {
+  static char pad[GNUNET_TESTING_HOSTKEYFILESIZE];
   FILE *f;
   struct GNUNET_CRYPTO_EccPrivateKey *pk;
   struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded *enc;
@@ -116,28 +90,34 @@
   {    
     fprintf (stderr,
 	     ".");
-    if (NULL == (pk = ecc_key_create ()))
+    if (NULL == (pk = GNUNET_CRYPTO_ecc_key_create ()))
     {
        GNUNET_break (0);
        break;
     }
     enc = GNUNET_CRYPTO_ecc_encode_key (pk);
-    if (htons (enc->size) != fwrite (enc, 1, htons (enc->size), f))
-      {
-	fprintf (stderr,
-		 _("\nFailed to write to `%s': %s\n"),
-		 fn,
-		 STRERROR (errno));
-	GNUNET_CRYPTO_ecc_key_free (pk);
-	GNUNET_free (enc);
-	break;
-      }
+    GNUNET_assert (ntohs (enc->size) <= GNUNET_TESTING_HOSTKEYFILESIZE);
+    if ( (ntohs (enc->size) != fwrite (enc, 1, ntohs (enc->size), f)) ||
+	 (GNUNET_TESTING_HOSTKEYFILESIZE - ntohs (enc->size)
+	  != fwrite (pad, 1, GNUNET_TESTING_HOSTKEYFILESIZE - ntohs (enc->size), f)) )
+    {
+      fprintf (stderr,
+	       _("\nFailed to write to `%s': %s\n"),
+	       fn,
+	       STRERROR (errno));
+      GNUNET_CRYPTO_ecc_key_free (pk);
+      GNUNET_free (enc);
+      break;
+    }
     GNUNET_CRYPTO_ecc_key_free (pk);
     GNUNET_free (enc);
   }
-  if (0 == make_keys)
+  if (UINT_MAX == make_keys)
     fprintf (stderr,
-	     _("Finished!\n"));
+	     _("\nFinished!\n"));
+  else
+    fprintf (stderr,
+	     _("\nError, %u keys not generated\n"), make_keys);
   fclose (f);
 }
 
Index: src/util/gnunet-rsa.c
===================================================================
--- src/util/gnunet-rsa.c	(revision 26008)
+++ src/util/gnunet-rsa.c	(working copy)
@@ -25,7 +25,6 @@
  */
 #include "platform.h"
 #include "gnunet_util_lib.h"
-#include "gnunet_testing_lib.h"
 #include <gcrypt.h>
 
 
@@ -49,109 +48,8 @@
  */
 static int weak_random;
 
-/**
- * Option set to create a bunch of keys at once.
- */
-static unsigned int make_keys;
 
 /**
- * The private information of an RSA key pair.
- * NOTE: this must match the definition in crypto_ksk.c and crypto_rsa.c!
- */
-struct GNUNET_CRYPTO_RsaPrivateKey
-{
-  gcry_sexp_t sexp;
-};
-
-
-/**
- * Create a new private key. Caller must free return value.
- *
- * @return fresh private key
- */
-static struct GNUNET_CRYPTO_RsaPrivateKey *
-rsa_key_create ()
-{
-  struct GNUNET_CRYPTO_RsaPrivateKey *ret;
-  gcry_sexp_t s_key;
-  gcry_sexp_t s_keyparam;
-
-  GNUNET_assert (0 ==
-                 gcry_sexp_build (&s_keyparam, NULL,
-                                  "(genkey(rsa(nbits %d)(rsa-use-e 3:257)))",
-                                  2048));
-  GNUNET_assert (0 == gcry_pk_genkey (&s_key, s_keyparam));
-  gcry_sexp_release (s_keyparam);
-#if EXTRA_CHECKS
-  GNUNET_assert (0 == gcry_pk_testkey (s_key));
-#endif
-  ret = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_RsaPrivateKey));
-  ret->sexp = s_key;
-  return ret;
-}
-
-
-/**
- * Create a flat file with a large number of key pairs for testing.
- */
-static void
-create_keys (const char *fn)
-{
-  FILE *f;
-  struct GNUNET_CRYPTO_RsaPrivateKey *pk;
-  struct GNUNET_CRYPTO_RsaPrivateKeyBinaryEncoded *enc;
-
-  if (NULL == (f = fopen (fn, "w+")))
-    {
-      fprintf (stderr,
-	       _("Failed to open `%s': %s\n"),
-	       fn,
-	       STRERROR (errno));
-      return;
-    }
-  fprintf (stderr,
-	   _("Generating %u keys, please wait"),
-	   make_keys);
-  while (0 < make_keys--)
-  {    
-    fprintf (stderr,
-	     ".");
-    if (NULL == (pk = rsa_key_create ()))
-    {
-       GNUNET_break (0);
-       break;
-    }
-    enc = GNUNET_CRYPTO_rsa_encode_key (pk);
-    if (GNUNET_TESTING_HOSTKEYFILESIZE != htons (enc->len))
-    {
-      /* sometimes we get a different key length because 'd' or 'u' start
-	 with leading bits; skip those... */
-      GNUNET_CRYPTO_rsa_key_free (pk);
-      GNUNET_free (enc);
-      make_keys++;
-      continue;
-    }
-    if (htons (enc->len) != fwrite (enc, 1, htons (enc->len), f))
-      {
-	fprintf (stderr,
-		 _("\nFailed to write to `%s': %s\n"),
-		 fn,
-		 STRERROR (errno));
-	GNUNET_CRYPTO_rsa_key_free (pk);
-	GNUNET_free (enc);
-	break;
-      }
-    GNUNET_CRYPTO_rsa_key_free (pk);
-    GNUNET_free (enc);
-  }
-  if (0 == make_keys)
-    fprintf (stderr,
-	     _("Finished!\n"));
-  fclose (f);
-}
-
-
-/**
  * Main function that will be run by the scheduler.
  *
  * @param cls closure
@@ -174,11 +72,6 @@
   }
   if (0 != weak_random)    
     GNUNET_CRYPTO_random_disable_entropy_gathering ();  
-  if (make_keys > 0)
-  {
-    create_keys (args[0]);
-    return;
-  }
   pk = GNUNET_CRYPTO_rsa_key_create_from_file (args[0]);
   if (NULL == pk)
     return;
@@ -225,9 +118,6 @@
 main (int argc, char *const*argv)
 {
   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
-    { 'g', "generate-keys", "COUNT",
-      gettext_noop ("create COUNT public-private key pairs (for testing)"),
-      1, &GNUNET_GETOPT_set_uint, &make_keys },
     { 'p', "print-public-key", NULL,
       gettext_noop ("print the public key in ASCII format"),
       0, &GNUNET_GETOPT_set_one, &print_public_key },
Index: src/util/crypto_ecc.c
===================================================================
--- src/util/crypto_ecc.c	(revision 26008)
+++ src/util/crypto_ecc.c	(working copy)
@@ -170,7 +170,7 @@
  * @return string representing  'pub'
  */
 char *
-GNUNET_CRYPTO_ecc_public_key_to_string (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *pub)
+GNUNET_CRYPTO_ecc_public_key_to_string (const struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *pub)
 {
   char *pubkeybuf;
   size_t keylen = (sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded)) * 8;
@@ -340,8 +340,9 @@
   if (len < sizeof (uint16_t)) 
     return NULL;
   memcpy (&be, buf, sizeof (be));
-  if (len != ntohs (be))
+  if (len < ntohs (be))
     return NULL;
+  len = ntohs (be);
   if (0 != (rc = gcry_sexp_sscan (&sexp,
 				  &erroff,
 				  &buf[2],
@@ -644,7 +645,7 @@
   GNUNET_assert (fs == GNUNET_DISK_file_read (fd, enc, fs));
   len = ntohs (enc->size);
   ret = NULL;
-  if ((len != fs) ||
+  if ((len > fs) ||
       (NULL == (ret = GNUNET_CRYPTO_ecc_decode_key ((char *) enc, len))))
   {
     LOG (GNUNET_ERROR_TYPE_ERROR,
Index: src/util/test_crypto_aes.c
===================================================================
--- src/util/test_crypto_aes.c	(revision 26008)
+++ src/util/test_crypto_aes.c	(working copy)
@@ -95,18 +95,7 @@
   ret = 0;
 
   memcpy (key.key, raw_key, GNUNET_CRYPTO_AES_KEY_LENGTH);
-  key.crc32 =
-      htonl (GNUNET_CRYPTO_crc32_n (&key, GNUNET_CRYPTO_AES_KEY_LENGTH));
 
-  if (ntohl (key.crc32) != (unsigned int) 38125195LL)
-  {
-    printf ("Static key has different CRC: %u - %u\n", ntohl (key.crc32),
-            key.crc32);
-
-    ret = 1;
-    goto error;
-  }
-
   if (GNUNET_CRYPTO_AES_KEY_LENGTH !=
       GNUNET_CRYPTO_aes_encrypt (plain, GNUNET_CRYPTO_AES_KEY_LENGTH, &key,
                                  (const struct
Index: src/util/crypto_hash.c
===================================================================
--- src/util/crypto_hash.c	(revision 26008)
+++ src/util/crypto_hash.c	(working copy)
@@ -419,8 +419,6 @@
                  GNUNET_CRYPTO_AES_KEY_LENGTH +
                  sizeof (struct GNUNET_CRYPTO_AesInitializationVector));
   memcpy (skey, hc, GNUNET_CRYPTO_AES_KEY_LENGTH);
-  skey->crc32 =
-      htonl (GNUNET_CRYPTO_crc32_n (skey, GNUNET_CRYPTO_AES_KEY_LENGTH));
   memcpy (iv, &((char *) hc)[GNUNET_CRYPTO_AES_KEY_LENGTH],
           sizeof (struct GNUNET_CRYPTO_AesInitializationVector));
 }
Index: src/util/crypto_aes.c
===================================================================
--- src/util/crypto_aes.c	(revision 26008)
+++ src/util/crypto_aes.c	(working copy)
@@ -42,33 +42,10 @@
 {
   gcry_randomize (&key->key[0], GNUNET_CRYPTO_AES_KEY_LENGTH,
                   GCRY_STRONG_RANDOM);
-  key->crc32 =
-      htonl (GNUNET_CRYPTO_crc32_n (key, GNUNET_CRYPTO_AES_KEY_LENGTH));
 }
 
 
 /**
- * Check that a new session key is well-formed.
- *
- * @return GNUNET_OK if the key is valid
- */
-int
-GNUNET_CRYPTO_aes_check_session_key (const struct GNUNET_CRYPTO_AesSessionKey
-                                     *key)
-{
-  uint32_t crc;
-
-  crc = GNUNET_CRYPTO_crc32_n (key, GNUNET_CRYPTO_AES_KEY_LENGTH);
-  if (ntohl (key->crc32) != crc)
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  return GNUNET_OK;
-}
-
-
-/**
  * Initialize AES cipher.
  *
  * @param handle handle to initialize
@@ -85,12 +62,6 @@
 {
   int rc;
 
-  if (GNUNET_OK !=
-      GNUNET_CRYPTO_aes_check_session_key (sessionkey))
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
   GNUNET_assert (0 ==
                  gcry_cipher_open (handle, GCRY_CIPHER_AES256,
                                    GCRY_CIPHER_MODE_CFB, 0));
Index: src/util/test_crypto_aes_weak.c
===================================================================
--- src/util/test_crypto_aes_weak.c	(revision 26008)
+++ src/util/test_crypto_aes_weak.c	(working copy)
@@ -91,9 +91,6 @@
   weak_key.key[30] = (char) (0xaa);
   weak_key.key[31] = (char) (0xaa);
   /* memset(&weak_key, 0, 32); */
-  weak_key.crc32 =
-      htonl (GNUNET_CRYPTO_crc32_n (&weak_key, GNUNET_CRYPTO_AES_KEY_LENGTH));
-
   size =
       GNUNET_CRYPTO_aes_encrypt (WEAK_KEY_TESTSTRING,
                                  strlen (WEAK_KEY_TESTSTRING) + 1, &weak_key,
Index: src/peerinfo-tool/gnunet-peerinfo.c
===================================================================
--- src/peerinfo-tool/gnunet-peerinfo.c	(revision 26008)
+++ src/peerinfo-tool/gnunet-peerinfo.c	(working copy)
@@ -167,7 +167,7 @@
 /**
  * My public key.
  */
-static struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key;
+static struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded my_public_key;
 
 /**
  * Head of list of print contexts.
@@ -524,7 +524,7 @@
 run (void *cls, char *const *args, const char *cfgfile,
      const struct GNUNET_CONFIGURATION_Handle *c)
 {
-  struct GNUNET_CRYPTO_RsaPrivateKey *priv;
+  struct GNUNET_CRYPTO_EccPrivateKey *priv;
   char *fn;
 
   cfg = c;
@@ -551,22 +551,22 @@
   {
     /* load private key */
     if (GNUNET_OK !=
-	GNUNET_CONFIGURATION_get_value_filename (cfg, "GNUNETD", "HOSTKEY",
+	GNUNET_CONFIGURATION_get_value_filename (cfg, "PEER", "PRIVATE_KEY",
 						 &fn))
     {
       FPRINTF (stderr, _("Could not find option `%s:%s' in configuration.\n"),
 	       "GNUNETD", "HOSTKEYFILE");
       return;
     }
-    if (NULL == (priv = GNUNET_CRYPTO_rsa_key_create_from_file (fn)))
+    if (NULL == (priv = GNUNET_CRYPTO_ecc_key_create_from_file (fn)))
     {
       FPRINTF (stderr, _("Loading hostkey from `%s' failed.\n"), fn);
       GNUNET_free (fn);
       return;
     }
     GNUNET_free (fn);
-    GNUNET_CRYPTO_rsa_key_get_public (priv, &my_public_key);
-    GNUNET_CRYPTO_rsa_key_free (priv);
+    GNUNET_CRYPTO_ecc_key_get_public (priv, &my_public_key);
+    GNUNET_CRYPTO_ecc_key_free (priv);
     GNUNET_CRYPTO_hash (&my_public_key, sizeof (my_public_key), &my_peer_identity.hashPubKey);
   }
 
Index: src/include/gnunet_protocols.h
===================================================================
--- src/include/gnunet_protocols.h	(revision 26008)
+++ src/include/gnunet_protocols.h	(working copy)
@@ -313,11 +313,6 @@
 #define GNUNET_MESSAGE_TYPE_CORE_PEER_CONNECTED 80
 
 /**
- * Session key exchange between peers.
- */
-#define GNUNET_MESSAGE_TYPE_CORE_SET_KEY 81
-
-/**
  * Encapsulation for an encrypted message between peers.
  */
 #define GNUNET_MESSAGE_TYPE_CORE_ENCRYPTED_MESSAGE 82
@@ -347,6 +342,12 @@
  */
 #define GNUNET_MESSAGE_TYPE_CORE_BINARY_TYPE_MAP 87
 
+/**
+ * Session key exchange between peers.
+ */
+#define GNUNET_MESSAGE_TYPE_CORE_EPHEMERAL_KEY 88
+
+
 /*******************************************************************************
  * DATASTORE message types
  ******************************************************************************/
Index: src/include/gnunet_crypto_lib.h
===================================================================
--- src/include/gnunet_crypto_lib.h	(revision 26008)
+++ src/include/gnunet_crypto_lib.h	(working copy)
@@ -323,10 +323,6 @@
    */
   unsigned char key[GNUNET_CRYPTO_AES_KEY_LENGTH];
 
-  /**
-   * checksum!
-   */
-  uint32_t crc32 GNUNET_PACKED;
 };
 GNUNET_NETWORK_STRUCT_END
 
@@ -451,17 +447,6 @@
 
 
 /**
- * Check that a new session key is well-formed.
- *
- * @param key key to check
- * @return GNUNET_OK if the key is valid
- */
-int
-GNUNET_CRYPTO_aes_check_session_key (const struct GNUNET_CRYPTO_AesSessionKey
-                                     *key);
-
-
-/**
  * Encrypt a block with the public key of another
  * host that uses the same cyper.
  *
@@ -1199,7 +1184,7 @@
  * @return string representing  'pub'
  */
 char *
-GNUNET_CRYPTO_ecc_public_key_to_string (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *pub);
+GNUNET_CRYPTO_ecc_public_key_to_string (const struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *pub);
 
 
 /**
Index: src/include/gnunet_signatures.h
===================================================================
--- src/include/gnunet_signatures.h	(revision 26008)
+++ src/include/gnunet_signatures.h	(working copy)
@@ -53,11 +53,6 @@
 #define GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DISCONNECT 2
 
 /**
- * Purpose is to set a session key.
- */
-#define GNUNET_SIGNATURE_PURPOSE_SET_KEY 3
-
-/**
  * Signature for a namespace/pseudonym advertisement (by
  * the namespace owner).
  */
@@ -115,12 +110,18 @@
  */
 #define GNUNET_SIGNATURE_PURPOSE_NSE_SEND 14
 
-
 /**
  * Signature of a gnunet naming system record block
  */
 #define GNUNET_SIGNATURE_PURPOSE_GNS_RECORD_SIGN 15
 
+/**
+ * Purpose is to set a session key.
+ */
+#define GNUNET_SIGNATURE_PURPOSE_SET_ECC_KEY 16
+
+
+
 #if 0                           /* keep Emacsens' auto-indent happy */
 {
 #endif
Index: src/include/gnunet_testing_lib.h
===================================================================
--- src/include/gnunet_testing_lib.h	(revision 26008)
+++ src/include/gnunet_testing_lib.h	(working copy)
@@ -44,7 +44,13 @@
 #endif
 #endif
 
-#define GNUNET_TESTING_HOSTKEYFILESIZE 914
+/**
+ * Size of each hostkey in the hostkey file (in BYTES).  This is the
+ * maximum length of the S-expressions generated by libgcrypt for the
+ * curves (rounded up to the next full KB to make IO nicer); it is NOT
+ * the number of bits in the key.
+ */
+#define GNUNET_TESTING_HOSTKEYFILESIZE 2048
 
 /**
  * Handle for a system on which GNUnet peers are executed;
@@ -138,7 +144,7 @@
  *        key; if NULL, GNUNET_SYSERR is returned immediately
  * @return NULL on error (not enough keys)
  */
-struct GNUNET_CRYPTO_RsaPrivateKey *
+struct GNUNET_CRYPTO_EccPrivateKey *
 GNUNET_TESTING_hostkey_get (const struct GNUNET_TESTING_System *system,
 			    uint32_t key_number,
 			    struct GNUNET_PeerIdentity *id);
Index: src/include/gnunet_hello_lib.h
===================================================================
--- src/include/gnunet_hello_lib.h	(revision 26008)
+++ src/include/gnunet_hello_lib.h	(working copy)
@@ -183,7 +183,7 @@
  * @return the hello message
  */
 struct GNUNET_HELLO_Message *
-GNUNET_HELLO_create (const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
+GNUNET_HELLO_create (const struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded
                      *publicKey,
                      GNUNET_HELLO_GenerateAddressListCallback addrgen,
                      void *addrgen_cls);
@@ -310,7 +310,7 @@
  */
 int
 GNUNET_HELLO_get_key (const struct GNUNET_HELLO_Message *hello,
-                      struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
+                      struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded
                       *publicKey);
 
 
@@ -364,7 +364,7 @@
  */
 int
 GNUNET_HELLO_parse_uri (const char *uri,
-                        struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *pubkey,
+                        struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *pubkey,
                         struct GNUNET_HELLO_Message **hello,
                         GNUNET_HELLO_TransportPluginsFind plugins_find);
 
Index: src/core/Makefile.am
===================================================================
--- src/core/Makefile.am	(revision 26008)
+++ src/core/Makefile.am	(working copy)
@@ -46,8 +46,6 @@
  gnunet-service-core_sessions.c gnunet-service-core_sessions.h \
  gnunet-service-core_typemap.c gnunet-service-core_typemap.h
 gnunet_service_core_LDADD = \
-  $(top_builddir)/src/hello/libgnunethello.la \
-  $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \
   $(top_builddir)/src/statistics/libgnunetstatistics.la \
   $(top_builddir)/src/transport/libgnunettransport.la \
   $(top_builddir)/src/util/libgnunetutil.la \
Index: src/core/gnunet-service-core_kx.c
===================================================================
--- src/core/gnunet-service-core_kx.c	(revision 26008)
+++ src/core/gnunet-service-core_kx.c	(working copy)
@@ -30,8 +30,6 @@
 #include "gnunet-service-core_neighbours.h"
 #include "gnunet-service-core_sessions.h"
 #include "gnunet_statistics_service.h"
-#include "gnunet_peerinfo_service.h"
-#include "gnunet_hello_lib.h"
 #include "gnunet_constants.h"
 #include "gnunet_signatures.h"
 #include "gnunet_protocols.h"
@@ -39,14 +37,9 @@
 
 
 /**
- * Set to GNUNET_YES to perform some slightly expensive internal invariant checks.
- */
-#define EXTRA_CHECKS GNUNET_YES
-
-/**
  * How long do we wait for SET_KEY confirmation initially?
  */
-#define INITIAL_SET_KEY_RETRY_FREQUENCY GNUNET_TIME_relative_multiply (MAX_SET_KEY_DELAY, 1)
+#define INITIAL_SET_KEY_RETRY_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
 
 /**
  * What is the minimum frequency for a PING message?
@@ -56,8 +49,12 @@
 /**
  * How often do we rekey?
  */
-#define REKEY_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 90)
+#define REKEY_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 12)
 
+/**
+ * What time difference do we tolerate?
+ */
+#define REKEY_TOLERANCE GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
 
 /**
  * What is the maximum age of a message for us to consider processing
@@ -68,14 +65,62 @@
  */
 #define MAX_MESSAGE_AGE GNUNET_TIME_UNIT_DAYS
 
+
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
 /**
- * What is the maximum delay for a SET_KEY message?
+ * Message transmitted with the signed ephemeral key of a peer.  The
+ * session key is then derived from the two ephemeral keys (ECDHE).
  */
-#define MAX_SET_KEY_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
+struct EphemeralKeyMessage
+{
 
+  /**
+   * Message type is CORE_EPHEMERAL_KEY.
+   */
+  struct GNUNET_MessageHeader header;
 
-GNUNET_NETWORK_STRUCT_BEGIN
+  /**
+   * Status of the sender (should be in "enum PeerStateMachine"), nbo.
+   */
+  int32_t sender_status GNUNET_PACKED;
 
+  /**
+   * An ECC signature of the 'origin' asserting the validity of
+   * the given ephemeral key.
+   */
+  struct GNUNET_CRYPTO_EccSignature signature;
+
+  /**
+   * Information about what is being signed.
+   */
+  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+
+  /**
+   * At what time was this key created (beginning of validity).
+   */
+  struct GNUNET_TIME_AbsoluteNBO creation_time;
+  
+  /**
+   * When does the given ephemeral key expire (end of validity).
+   */
+  struct GNUNET_TIME_AbsoluteNBO expiration_time;
+
+  /**
+   * 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;  
+
+  /**
+   * Public key of the signing peer (persistent version, not the ephemeral public key).
+   */
+  struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded origin_public_key;
+
+};
+
+
 /**
  * We're sending an (encrypted) PING to the other peer to check if he
  * can decrypt.  The other peer should respond with a PONG with the
@@ -128,9 +173,9 @@
   uint32_t challenge GNUNET_PACKED;
 
   /**
-   * Reserved, always 'GNUNET_BANDWIDTH_VALUE_MAX'.
+   * Reserved, always zero.
    */
-  struct GNUNET_BANDWIDTH_Value32NBO reserved;
+  uint32_t reserved;
 
   /**
    * Intended target of the PING, used primarily to check
@@ -141,51 +186,6 @@
 
 
 /**
- * Message transmitted to set (or update) a session key.
- */
-struct SetKeyMessage
-{
-
-  /**
-   * Message type is either CORE_SET_KEY.
-   */
-  struct GNUNET_MessageHeader header;
-
-  /**
-   * Status of the sender (should be in "enum PeerStateMachine"), nbo.
-   */
-  int32_t sender_status GNUNET_PACKED;
-
-  /**
-   * Purpose of the signature, will be
-   * GNUNET_SIGNATURE_PURPOSE_SET_KEY.
-   */
-  struct GNUNET_CRYPTO_RsaSignaturePurpose purpose;
-
-  /**
-   * At what time was this key created?
-   */
-  struct GNUNET_TIME_AbsoluteNBO creation_time;
-
-  /**
-   * The encrypted session key.
-   */
-  struct GNUNET_CRYPTO_RsaEncryptedData encrypted_key;
-
-  /**
-   * Who is the intended recipient?
-   */
-  struct GNUNET_PeerIdentity target;
-
-  /**
-   * Signature of the stuff above (starting at purpose).
-   */
-  struct GNUNET_CRYPTO_RsaSignature signature;
-
-};
-
-
-/**
  * Encapsulation for encrypted messages exchanged between
  * peers.  Followed by the actual encrypted data.
  */
@@ -216,9 +216,9 @@
   uint32_t sequence_number GNUNET_PACKED;
 
   /**
-   * Reserved, always 'GNUNET_BANDWIDTH_VALUE_MAX'.
+   * Reserved, always zero.
    */
-  struct GNUNET_BANDWIDTH_Value32NBO reserved;
+  uint32_t reserved;
 
   /**
    * Timestamp.  Used to prevent reply of ancient messages
@@ -263,23 +263,18 @@
   KX_STATE_KEY_RECEIVED,
 
   /**
-   * The other peer has confirmed our session key with a message
-   * encrypted with his session key (which we got).  Key exchange
-   * is done.
+   * The other peer has confirmed our session key + PING with a PONG
+   * message encrypted with his session key (which we got).  Key
+   * exchange is done.
    */
   KX_STATE_UP,
 
   /**
-   * We're rekeying, so we have received the other peer's session
-   * key, but he didn't get ours yet.
+   * We're rekeying, so we have sent the other peer our new ephemeral
+   * key, but we did not get a matching PONG yet.
    */
-  KX_STATE_REKEY,
+  KX_STATE_REKEY_SENT
 
-  /**
-   * We're rekeying but have not yet received confirmation for our new
-   * key from the other peer.
-   */
-  KX_STATE_REKEY_SENT
 };
 
 
@@ -288,64 +283,28 @@
  */
 struct GSC_KeyExchangeInfo
 {
-  /**
-   * Identity of the peer.
-   */
-  struct GNUNET_PeerIdentity peer;
 
   /**
-   * SetKeyMessage to transmit (initialized the first
-   * time our status goes past 'KX_STATE_KEY_SENT').
+   * DLL.
    */
-  struct SetKeyMessage skm;
+  struct GSC_KeyExchangeInfo *next;
 
   /**
-   * PING message we transmit to the other peer.
+   * DLL.
    */
-  struct PingMessage ping;
+  struct GSC_KeyExchangeInfo *prev;
 
   /**
-   * SetKeyMessage we received and did not process yet.
+   * Identity of the peer.
    */
-  struct SetKeyMessage *skm_received;
+  struct GNUNET_PeerIdentity peer;
 
   /**
-   * PING message we received from the other peer and
-   * did not process yet (or NULL).
+   * PING message we transmit to the other peer.
    */
-  struct PingMessage *ping_received;
+  struct PingMessage ping;
 
   /**
-   * PONG message we received from the other peer and
-   * did not process yet (or NULL).
-   */
-  struct PongMessage *pong_received;
-
-  /**
-   * Encrypted message we received from the other peer and
-   * did not process yet (or NULL).
-   */
-  struct EncryptedMessage *emsg_received;
-
-  /**
-   * Non-NULL if we are currently looking up HELLOs for this peer.
-   * for this peer.
-   */
-  struct GNUNET_PEERINFO_IteratorContext *pitr;
-
-  /**
-   * Public key of the neighbour, NULL if we don't have it yet.
-   */
-  struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *public_key;
-
-  /**
-   * We received a PONG message before we got the "public_key"
-   * (or the SET_KEY).  We keep it here until we have a key
-   * to decrypt it.  NULL if no PONG is pending.
-   */
-  struct PongMessage *pending_pong;
-
-  /**
    * Key we use to encrypt our messages for the other peer
    * (initialized by us when we do the handshake).
    */
@@ -358,14 +317,9 @@
   struct GNUNET_CRYPTO_AesSessionKey decrypt_key;
 
   /**
-   * At what time did we generate our encryption key?
-   */
-  struct GNUNET_TIME_Absolute encrypt_key_created;
-
-  /**
    * At what time did the other peer generate the decryption key?
    */
-  struct GNUNET_TIME_Absolute decrypt_key_created;
+  struct GNUNET_TIME_Absolute foreign_key_expires;
 
   /**
    * When should the session time out (if there are no PONGs)?
@@ -418,77 +372,62 @@
 
 
 /**
- * Handle to peerinfo service.
+ * Our private key.
  */
-static struct GNUNET_PEERINFO_Handle *peerinfo;
+static struct GNUNET_CRYPTO_EccPrivateKey *my_private_key;
 
 /**
- * Our private key.
+ * Our ephemeral private key.
  */
-static struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key;
+static struct GNUNET_CRYPTO_EccPrivateKey *my_ephemeral_key;
 
 /**
+ * Current message we send for a key exchange.
+ */
+static struct EphemeralKeyMessage current_ekm;
+
+/**
  * Our public key.
  */
-static struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key;
+static struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded my_public_key;
 
 /**
  * Our message stream tokenizer (for encrypted payload).
  */
 static struct GNUNET_SERVER_MessageStreamTokenizer *mst;
 
+/**
+ * DLL head.
+ */
+static struct GSC_KeyExchangeInfo *kx_head;
 
-#if EXTRA_CHECKS
 /**
- * Check internal invariants of the given KX record.
- *
- * @param kx record to check
- * @param file filename for error reporting
- * @param line line number for error reporting
+ * DLL tail.
+ */
+static struct GSC_KeyExchangeInfo *kx_tail;
+
+/**
+ * Task scheduled for periodic re-generation (and thus rekeying) of our
+ * ephemeral key.
  */ 
-static void
-check_kx_record (struct GSC_KeyExchangeInfo *kx,
-		 const char *file,
-		 int line)
-{
-  struct GNUNET_HashCode hc;
+static GNUNET_SCHEDULER_TaskIdentifier rekey_task;
 
-  if (NULL == kx->public_key)
-    return;
-  GNUNET_CRYPTO_hash (kx->public_key, sizeof (*kx->public_key), &hc);
-  GNUNET_assert_at (0 == memcmp (&hc, &kx->peer, sizeof (struct GNUNET_HashCode)), file, line);
-}
 
-
 /**
- * Check internal invariants of the given KX record.
- *
- * @param kx record to check
- */
-#define CHECK_KX(kx) check_kx_record(kx, __FILE__, __LINE__)
-#else
-#define CHECK_KX(kx) 
-#endif
-
-/**
  * Derive an authentication key from "set key" information
  *
  * @param akey authentication key to derive
  * @param skey session key to use
  * @param seed seed to use
- * @param creation_time creation time to use
  */
 static void
 derive_auth_key (struct GNUNET_CRYPTO_AuthKey *akey,
-                 const struct GNUNET_CRYPTO_AesSessionKey *skey, uint32_t seed,
-                 struct GNUNET_TIME_Absolute creation_time)
+                 const struct GNUNET_CRYPTO_AesSessionKey *skey, uint32_t seed)
 {
   static const char ctx[] = "authentication key";
-  struct GNUNET_TIME_AbsoluteNBO ctbe;
 
-  ctbe = GNUNET_TIME_absolute_hton (creation_time);
   GNUNET_CRYPTO_hmac_derive_key (akey, skey, &seed, sizeof (seed), &skey->key,
-                                 sizeof (skey->key), &ctbe, sizeof (ctbe), ctx,
+                                 sizeof (skey->key), ctx,
                                  sizeof (ctx), NULL);
 }
 
@@ -539,6 +478,31 @@
 
 
 /**
+ * Derive an AES key from key material
+ *
+ * @param sender peer identity of the sender
+ * @param receiver peer identity of the sender
+ * @param key_material high entropy key material to use
+ * @param skey set to derived session key 
+ */
+static void
+derive_aes_key (const struct GNUNET_PeerIdentity *sender,
+		const struct GNUNET_PeerIdentity *receiver,
+		const struct GNUNET_HashCode *key_material,
+		struct GNUNET_CRYPTO_AesSessionKey *skey)
+{
+  static const char ctx[] = "aes key generation vector";
+
+  GNUNET_CRYPTO_kdf (skey, sizeof (struct GNUNET_CRYPTO_AesSessionKey),
+		     ctx, sizeof (ctx),
+		     skey, sizeof (struct GNUNET_CRYPTO_AesSessionKey),
+		     sender, sizeof (struct GNUNET_PeerIdentity),
+		     receiver, sizeof (struct GNUNET_PeerIdentity),
+		     NULL);
+}
+
+
+/**
  * Encrypt size bytes from in and write the result to out.  Use the
  * key for outbound traffic of the given neighbour.
  *
@@ -601,8 +565,7 @@
     return GNUNET_NO;
   }
   if ( (kx->status != KX_STATE_KEY_RECEIVED) && (kx->status != KX_STATE_UP) &&
-       (kx->status != KX_STATE_REKEY_SENT) &&
-       (kx->status != KX_STATE_REKEY) )
+       (kx->status != KX_STATE_REKEY_SENT) )
   {
     GNUNET_break_op (0);
     return GNUNET_SYSERR;
@@ -651,88 +614,34 @@
   struct GSC_KeyExchangeInfo *kx = cls;
 
   kx->retry_set_key_task = GNUNET_SCHEDULER_NO_TASK;
-  kx->set_key_retry_frequency =
-      GNUNET_TIME_relative_multiply (kx->set_key_retry_frequency, 2);
+  kx->set_key_retry_frequency = GNUNET_TIME_STD_BACKOFF (kx->set_key_retry_frequency);
   send_key (kx);
 }
 
 
 /**
- * PEERINFO is giving us a HELLO for a peer.  Add the public key to
- * the neighbour's struct and continue with the key exchange.  Or, if
- * we did not get a HELLO, just do nothing.
+ * Create a fresh PING message for transmission to the other peer.
  *
- * @param cls the 'struct GSC_KeyExchangeInfo' to retry sending the key for
- * @param peer the peer for which this is the HELLO
- * @param hello HELLO message of that peer
- * @param err_msg NULL if successful, otherwise contains error message
+ * @param kx key exchange context to create PING for
  */
 static void
-process_hello (void *cls, const struct GNUNET_PeerIdentity *peer,
-               const struct GNUNET_HELLO_Message *hello, const char *err_msg)
+setup_fresh_ping (struct GSC_KeyExchangeInfo *kx)
 {
-  struct GSC_KeyExchangeInfo *kx = cls;
-  struct SetKeyMessage *skm;
+  struct PingMessage pp;
+  struct PingMessage *pm;
+  struct GNUNET_CRYPTO_AesInitializationVector iv;
 
-  CHECK_KX (kx);
-  if (NULL != err_msg)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                _("Error in communication with PEERINFO service\n"));
-    kx->pitr = NULL;
-    if (GNUNET_SCHEDULER_NO_TASK != kx->retry_set_key_task)
-      GNUNET_SCHEDULER_cancel (kx->retry_set_key_task);
-    kx->retry_set_key_task =
-        GNUNET_SCHEDULER_add_delayed (kx->set_key_retry_frequency,
-                                      &set_key_retry_task, kx);
-    return;
-  }
-  if (NULL == peer)
-  {
-    kx->pitr = NULL;
-    if (NULL != kx->public_key)
-      return;                   /* done here */
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Failed to obtain public key for peer `%4s', delaying processing of SET_KEY\n",
-                GNUNET_i2s (&kx->peer));
-    GNUNET_STATISTICS_update (GSC_stats,
-                              gettext_noop
-                              ("# Delayed connecting due to lack of public key"),
-                              1, GNUNET_NO);
-    if (GNUNET_SCHEDULER_NO_TASK != kx->retry_set_key_task)
-      GNUNET_SCHEDULER_cancel (kx->retry_set_key_task);
-    kx->retry_set_key_task =
-        GNUNET_SCHEDULER_add_delayed (kx->set_key_retry_frequency,
-                                      &set_key_retry_task, kx);
-    return;
-  }
-  GNUNET_break (0 == memcmp (peer, &kx->peer, sizeof (struct GNUNET_PeerIdentity)));
-  if (NULL != kx->public_key)
-  {
-    /* already have public key, why are we here? */
-    GNUNET_break (0);
-    return;
-  }
-  GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == kx->retry_set_key_task);
-  kx->public_key =
-      GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
-  if (GNUNET_OK != GNUNET_HELLO_get_key (hello, kx->public_key))
-  {
-    GNUNET_break (0);
-    GNUNET_free (kx->public_key);
-    kx->public_key = NULL;
-    CHECK_KX (kx);
-    return;
-  }
-  CHECK_KX (kx);
-  send_key (kx);
-  if (NULL != kx->skm_received)
-  {
-    skm = kx->skm_received;
-    kx->skm_received = NULL;
-    GSC_KX_handle_set_key (kx, &skm->header);
-    GNUNET_free (skm);
-  }
+  pm = &kx->ping;
+  pm->header.size = htons (sizeof (struct PingMessage));
+  pm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_PING);
+  pm->iv_seed =
+      GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX);
+  derive_iv (&iv, &kx->encrypt_key, pm->iv_seed, &kx->peer);
+  pp.challenge = kx->ping_challenge;
+  pp.target = kx->peer;
+  do_encrypt (kx, &iv, &pp.target, &pm->target,
+              sizeof (struct PingMessage) - ((void *) &pm->target -
+                                             (void *) pm));
 }
 
 
@@ -747,7 +656,8 @@
 {
   struct GSC_KeyExchangeInfo *kx;
 
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Initiating key exchange with `%s'\n",
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
+	      "Initiating key exchange with `%s'\n",
               GNUNET_i2s (pid));
   GNUNET_STATISTICS_update (GSC_stats,
                             gettext_noop ("# key exchanges initiated"), 1,
@@ -755,11 +665,10 @@
   kx = GNUNET_malloc (sizeof (struct GSC_KeyExchangeInfo));
   kx->peer = *pid;
   kx->set_key_retry_frequency = INITIAL_SET_KEY_RETRY_FREQUENCY;
-  kx->pitr =
-      GNUNET_PEERINFO_iterate (peerinfo, pid,
-                               GNUNET_TIME_UNIT_FOREVER_REL /* timeout? */ ,
-                               &process_hello, kx);
-  CHECK_KX (kx);
+  GNUNET_CONTAINER_DLL_insert (kx_head,
+			       kx_tail,
+			       kx);
+  send_key (kx);
   return kx;
 }
 
@@ -774,11 +683,6 @@
 {
   GNUNET_STATISTICS_update (GSC_stats, gettext_noop ("# key exchanges stopped"),
                             1, GNUNET_NO);
-  if (NULL != kx->pitr)
-  {
-    GNUNET_PEERINFO_iterate_cancel (kx->pitr);
-    kx->pitr = NULL;
-  }
   if (kx->retry_set_key_task != GNUNET_SCHEDULER_NO_TASK)
   {
     GNUNET_SCHEDULER_cancel (kx->retry_set_key_task);
@@ -789,16 +693,26 @@
     GNUNET_SCHEDULER_cancel (kx->keep_alive_task);
     kx->keep_alive_task = GNUNET_SCHEDULER_NO_TASK;
   }
-  GNUNET_free_non_null (kx->skm_received);
-  GNUNET_free_non_null (kx->ping_received);
-  GNUNET_free_non_null (kx->pong_received);
-  GNUNET_free_non_null (kx->emsg_received);
-  GNUNET_free_non_null (kx->public_key);
+  GNUNET_CONTAINER_DLL_remove (kx_head,
+			       kx_tail,
+			       kx);
   GNUNET_free (kx);
 }
 
 
 /**
+ * Send our PING to the other peer.
+ *
+ * @param kx key exchange context
+ */
+static void
+send_ping (struct GSC_KeyExchangeInfo *kx)
+{
+  GSC_NEIGHBOURS_transmit (&kx->peer, &kx->ping.header,
+                           MIN_PING_FREQUENCY);
+}
+
+/**
  * We received a SET_KEY message.  Validate and update
  * our key material and status.
  *
@@ -806,146 +720,142 @@
  * @param msg the set key message we received
  */
 void
-GSC_KX_handle_set_key (struct GSC_KeyExchangeInfo *kx,
-                       const struct GNUNET_MessageHeader *msg)
+GSC_KX_handle_ephemeral_key (struct GSC_KeyExchangeInfo *kx,
+			     const struct GNUNET_MessageHeader *msg)
 {
-  const struct SetKeyMessage *m;
-  struct GNUNET_TIME_Absolute t;
-  struct GNUNET_CRYPTO_AesSessionKey k;
-  struct PingMessage *ping;
-  struct PongMessage *pong;
-  enum KxStateMachine sender_status;
+  const struct EphemeralKeyMessage *m;
+  struct GNUNET_TIME_Absolute start_t;
+  struct GNUNET_TIME_Absolute end_t;
+  struct GNUNET_TIME_Absolute now;
+  struct GNUNET_PeerIdentity signer_id;
+  enum KxStateMachine sender_status;  
   uint16_t size;
+  struct GNUNET_HashCode key_material;
   
-  CHECK_KX (kx);
   size = ntohs (msg->size);
-  if (size != sizeof (struct SetKeyMessage))
+  if (sizeof (struct EphemeralKeyMessage) != size)
   {
     GNUNET_break_op (0);
     return;
   }
-  m = (const struct SetKeyMessage *) msg;
-  GNUNET_STATISTICS_update (GSC_stats, gettext_noop ("# session keys received"),
+  m = (const struct EphemeralKeyMessage *) msg;
+  GNUNET_STATISTICS_update (GSC_stats, gettext_noop ("# ephemeral keys received"),
                             1, GNUNET_NO);
 
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Core service receives `%s' request from `%4s'.\n", "SET_KEY",
+              "Core service receives `%s' request from `%4s'.\n", "EPHEMERAL_KEY",
               GNUNET_i2s (&kx->peer));
-  if (NULL == kx->public_key)
-  {
-    GNUNET_free_non_null (kx->skm_received);
-    kx->skm_received = (struct SetKeyMessage *) GNUNET_copy_message (msg);
-    return;
-  }
+  GNUNET_CRYPTO_hash (&m->origin_public_key,
+		      sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded),
+		      &signer_id.hashPubKey);
   if (0 !=
-      memcmp (&m->target, &GSC_my_identity,
+      memcmp (&signer_id, &kx->peer,
               sizeof (struct GNUNET_PeerIdentity)))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                _("`%s' is for `%s', not for me.  Ignoring.\n"), "SET_KEY",
-                GNUNET_i2s (&m->target));
+  {    
+    GNUNET_break_op (0);
     return;
   }
   if ((ntohl (m->purpose.size) !=
-       sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) +
+       sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
        sizeof (struct GNUNET_TIME_AbsoluteNBO) +
-       sizeof (struct GNUNET_CRYPTO_RsaEncryptedData) +
-       sizeof (struct GNUNET_PeerIdentity)) ||
+       sizeof (struct GNUNET_TIME_AbsoluteNBO) +
+       sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded) +
+       sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded)) ||
       (GNUNET_OK !=
-       GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_SET_KEY, &m->purpose,
-                                 &m->signature, kx->public_key)))
+       GNUNET_CRYPTO_ecc_verify (GNUNET_SIGNATURE_PURPOSE_SET_ECC_KEY,
+				 &m->purpose,
+                                 &m->signature, &m->origin_public_key)))
   {
     /* invalid signature */
-    CHECK_KX (kx);
     GNUNET_break_op (0);
     return;
   }
-  t = GNUNET_TIME_absolute_ntoh (m->creation_time);
-  if (((kx->status == KX_STATE_KEY_RECEIVED) || (kx->status == KX_STATE_UP)) &&
-      (t.abs_value < kx->decrypt_key_created.abs_value))
+  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) )
   {
-    /* this could rarely happen due to massive re-ordering of
-     * messages on the network level, but is most likely either
-     * a bug or some adversary messing with us.  Report. */
-    GNUNET_break_op (0);
+    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);
     return;
   }
-  if ((GNUNET_CRYPTO_rsa_decrypt
-       (my_private_key, &m->encrypted_key, &k,
-        sizeof (struct GNUNET_CRYPTO_AesSessionKey)) !=
-       sizeof (struct GNUNET_CRYPTO_AesSessionKey)) ||
-      (GNUNET_OK != GNUNET_CRYPTO_aes_check_session_key (&k)))
+  if (GNUNET_OK !=
+      GNUNET_CRYPTO_ecc_ecdh (my_ephemeral_key,
+			      &m->ephemeral_key,			      
+			      &key_material))
   {
-    /* failed to decrypt !? */
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-		"Invalid key %x decrypted by %s from message %u (origin: %s)\n",
-		(unsigned int) GNUNET_CRYPTO_crc32_n (&k, sizeof (struct GNUNET_CRYPTO_AesSessionKey)),
-		GNUNET_i2s (&GSC_my_identity),
-		(unsigned int) GNUNET_CRYPTO_crc32_n (&m->encrypted_key, sizeof (struct GNUNET_CRYPTO_RsaEncryptedData)),
-		GNUNET_h2s (&kx->peer.hashPubKey));
-    GNUNET_break_op (0);
+    GNUNET_break (0);
     return;
   }
   GNUNET_STATISTICS_update (GSC_stats,
-                            gettext_noop ("# SET_KEY messages decrypted"), 1,
+                            gettext_noop ("# EPHEMERAL_KEY messages decrypted"), 1,
                             GNUNET_NO);
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received SET_KEY from `%s'\n",
-              GNUNET_i2s (&kx->peer));
-  kx->decrypt_key = k;
-  if (kx->decrypt_key_created.abs_value != t.abs_value)
+  derive_aes_key (&GSC_my_identity,
+		  &kx->peer,
+		  &key_material,
+		  &kx->encrypt_key);
+  derive_aes_key (&kx->peer,
+		  &GSC_my_identity,
+		  &key_material,
+		  &kx->decrypt_key);
+  /* fresh key, reset sequence numbers */
+  kx->last_sequence_number_received = 0;
+  kx->last_packets_bitmap = 0;
+  kx->foreign_key_expires = end_t;
+  setup_fresh_ping (kx);
+
+  /* check if we still need to send the sender our key */
+  sender_status = (enum KxStateMachine) ntohl (m->sender_status);  
+  switch (sender_status)
   {
-    /* fresh key, reset sequence numbers */
-    kx->last_sequence_number_received = 0;
-    kx->last_packets_bitmap = 0;
-    kx->decrypt_key_created = t;
+  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);
+    break;
+  case KX_STATE_KEY_RECEIVED:
+  case KX_STATE_UP: 
+  case KX_STATE_REKEY_SENT:
+    /* other peer already got our key */
+    break;
+  default:
+    GNUNET_break (0);
+    break;
   }
-  sender_status = (enum KxStateMachine) ntohl (m->sender_status);
+  /* check if we need to confirm everything is fine via PING + PONG */
   switch (kx->status)
   {
   case KX_STATE_DOWN:
     kx->status = KX_STATE_KEY_RECEIVED;
-    /* we're not up, so we are already doing 'send_key' */
+    send_ping (kx);
     break;
   case KX_STATE_KEY_SENT:
     kx->status = KX_STATE_KEY_RECEIVED;
-    /* we're not up, so we are already doing 'send_key' */
+    send_ping (kx);
     break;
   case KX_STATE_KEY_RECEIVED:
-    /* we're not up, so we are already doing 'send_key' */
+    send_ping (kx);
     break;
   case KX_STATE_UP: 
-    if ((sender_status == KX_STATE_DOWN) ||
-        (sender_status == KX_STATE_KEY_SENT))
-      send_key (kx);            /* we are up, but other peer is not! */
+    kx->status = KX_STATE_KEY_RECEIVED;
+    /* we got a new key, need to reconfirm! */
+    send_ping (kx);
     break;
-  case KX_STATE_REKEY:
-    if ((sender_status == KX_STATE_DOWN) ||
-        (sender_status == KX_STATE_KEY_SENT))
-      send_key (kx);            /* we are up, but other peer is not! */
-    break;
   case KX_STATE_REKEY_SENT:
-    if ((sender_status == KX_STATE_DOWN) ||
-        (sender_status == KX_STATE_KEY_SENT))
-      send_key (kx);            /* we are up, but other peer is not! */
+    kx->status = KX_STATE_KEY_RECEIVED;
+    /* we got a new key, need to reconfirm! */
+    send_ping (kx);
     break;
   default:
     GNUNET_break (0);
     break;
   }
-  if (NULL != kx->ping_received)
-  {
-    ping = kx->ping_received;
-    kx->ping_received = NULL;
-    GSC_KX_handle_ping (kx, &ping->header);
-    GNUNET_free (ping);
-  }
-  if (NULL != kx->pong_received)
-  {
-    pong = kx->pong_received;
-    kx->pong_received = NULL;
-    GSC_KX_handle_pong (kx, &pong->header);
-    GNUNET_free (pong);
-  }
 }
 
 
@@ -976,12 +886,14 @@
   GNUNET_STATISTICS_update (GSC_stats,
                             gettext_noop ("# PING messages received"), 1,
                             GNUNET_NO);
-  if ((kx->status != KX_STATE_KEY_RECEIVED) && (kx->status != KX_STATE_UP) &&
-      (kx->status != KX_STATE_REKEY_SENT))
+  if ( (kx->status != KX_STATE_KEY_RECEIVED) && 
+       (kx->status != KX_STATE_UP) &&
+       (kx->status != KX_STATE_REKEY_SENT))
   {
-    /* defer */
-    GNUNET_free_non_null (kx->ping_received);
-    kx->ping_received = (struct PingMessage *) GNUNET_copy_message (msg);
+    /* ignore */
+    GNUNET_STATISTICS_update (GSC_stats,
+			      gettext_noop ("# PING messages dropped (out of order)"), 1,
+			      GNUNET_NO);
     return;
   }
   m = (const struct PingMessage *) msg;
@@ -1012,10 +924,8 @@
     GNUNET_break_op (0);
     return;
   }
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received PING from `%s'\n",
-              GNUNET_i2s (&kx->peer));
   /* construct PONG */
-  tx.reserved = GNUNET_BANDWIDTH_VALUE_MAX;
+  tx.reserved = 0;
   tx.challenge = t.challenge;
   tx.target = t.target;
   tp.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_PONG);
@@ -1034,76 +944,6 @@
 
 
 /**
- * Create a fresh SET KEY message for transmission to the other peer.
- * Also creates a new key.
- *
- * @param kx key exchange context to create SET KEY message for
- */
-static void
-setup_fresh_setkey (struct GSC_KeyExchangeInfo *kx)
-{
-  struct SetKeyMessage *skm;
-
-  GNUNET_CRYPTO_aes_create_session_key (&kx->encrypt_key);
-  kx->encrypt_key_created = GNUNET_TIME_absolute_get ();
-  skm = &kx->skm;
-  skm->header.size = htons (sizeof (struct SetKeyMessage));
-  skm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_SET_KEY);
-  skm->purpose.size =
-      htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) +
-             sizeof (struct GNUNET_TIME_AbsoluteNBO) +
-             sizeof (struct GNUNET_CRYPTO_RsaEncryptedData) +
-             sizeof (struct GNUNET_PeerIdentity));
-  skm->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_SET_KEY);
-  skm->creation_time = GNUNET_TIME_absolute_hton (kx->encrypt_key_created);
-  skm->target = kx->peer;
-  CHECK_KX (kx);
-  GNUNET_assert (GNUNET_OK ==
-                 GNUNET_CRYPTO_rsa_encrypt (&kx->encrypt_key,
-                                            sizeof (struct
-                                                    GNUNET_CRYPTO_AesSessionKey),
-                                            kx->public_key,
-                                            &skm->encrypted_key));
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-	      "Encrypting key %x for %s resulting in message %u (origin: %s)\n",
-	      (unsigned int) GNUNET_CRYPTO_crc32_n (&kx->encrypt_key, sizeof (struct GNUNET_CRYPTO_AesSessionKey)),
-	      GNUNET_i2s (&kx->peer),
-	      (unsigned int) GNUNET_CRYPTO_crc32_n (&skm->encrypted_key, sizeof (struct GNUNET_CRYPTO_RsaEncryptedData)),
-	      GNUNET_h2s (&GSC_my_identity.hashPubKey));
-
-  GNUNET_assert (GNUNET_OK ==
-                 GNUNET_CRYPTO_rsa_sign (my_private_key, &skm->purpose,
-                                         &skm->signature));
-}
-
-
-/**
- * Create a fresh PING message for transmission to the other peer.
- *
- * @param kx key exchange context to create PING for
- */
-static void
-setup_fresh_ping (struct GSC_KeyExchangeInfo *kx)
-{
-  struct PingMessage pp;
-  struct PingMessage *pm;
-  struct GNUNET_CRYPTO_AesInitializationVector iv;
-
-  pm = &kx->ping;
-  pm->header.size = htons (sizeof (struct PingMessage));
-  pm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_PING);
-  pm->iv_seed =
-      GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX);
-  derive_iv (&iv, &kx->encrypt_key, pm->iv_seed, &kx->peer);
-  pp.challenge = kx->ping_challenge;
-  pp.target = kx->peer;
-  do_encrypt (kx, &iv, &pp.target, &pm->target,
-              sizeof (struct PingMessage) - ((void *) &pm->target -
-                                             (void *) pm));
-}
-
-
-/**
  * Task triggered when a neighbour entry is about to time out
  * (and we should prevent this by sending a PING).
  *
@@ -1168,49 +1008,6 @@
 
 
 /**
- * Trigger rekeying event.
- * 
- * @param cls the 'struct GSC_KeyExchangeInfo'
- * @param tc schedule context (unused)
- */
-static void
-trigger_rekey (void *cls,
-	       const struct GNUNET_SCHEDULER_TaskContext *tc)
-{
-  struct GSC_KeyExchangeInfo *kx = cls;
-  
-  GNUNET_break (KX_STATE_UP == kx->status);
-  kx->status = KX_STATE_REKEY;
-  kx->set_key_retry_frequency = INITIAL_SET_KEY_RETRY_FREQUENCY;
-  kx->retry_set_key_task =
-    GNUNET_SCHEDULER_add_delayed (kx->set_key_retry_frequency,
-				  &set_key_retry_task, kx);
-}
-
-
-/**
- * Schedule rekey operation.
- *
- * @param kx key exchange to schedule rekey for
- */
-static void
-schedule_rekey (struct GSC_KeyExchangeInfo *kx)
-{
-  struct GNUNET_TIME_Relative rdelay;
-
-  if (GNUNET_SCHEDULER_NO_TASK != kx->retry_set_key_task)  
-    GNUNET_SCHEDULER_cancel (kx->retry_set_key_task);
-  rdelay = REKEY_FREQUENCY;
-  /* randomize rekey frequency by one minute to avoid synchronization */
-  rdelay.rel_value += GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
-						60 * 1000);
-  kx->retry_set_key_task = GNUNET_SCHEDULER_add_delayed (REKEY_FREQUENCY,
-							 &trigger_rekey,
-							 kx);   
-}
-
-
-/**
  * We received a PONG message.  Validate and update our status.
  *
  * @param kx key exchange context for the the PONG
@@ -1222,7 +1019,6 @@
 {
   const struct PongMessage *m;
   struct PongMessage t;
-  struct EncryptedMessage *emsg;
   struct GNUNET_CRYPTO_AesInitializationVector iv;
   uint16_t msize;
 
@@ -1238,17 +1034,19 @@
   switch (kx->status)
   {
   case KX_STATE_DOWN:
+    GNUNET_STATISTICS_update (GSC_stats,
+			      gettext_noop ("# PONG messages dropped (connection down)"), 1,
+			      GNUNET_NO);
     return;
   case KX_STATE_KEY_SENT:
-    GNUNET_free_non_null (kx->pong_received);
-    kx->pong_received = (struct PongMessage *) GNUNET_copy_message (msg);    
+    GNUNET_STATISTICS_update (GSC_stats,
+			      gettext_noop ("# PONG messages dropped (out of order)"), 1,
+			      GNUNET_NO);
     return;
   case KX_STATE_KEY_RECEIVED:
     break;
   case KX_STATE_UP:
     break;
-  case KX_STATE_REKEY:
-    break;
   case KX_STATE_REKEY_SENT:
     break;
   default:
@@ -1289,13 +1087,19 @@
   }
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received PONG from `%s'\n",
               GNUNET_i2s (&kx->peer));
+  /* no need to resend key any longer */
+  if (GNUNET_SCHEDULER_NO_TASK != kx->retry_set_key_task)
+  {
+    GNUNET_SCHEDULER_cancel (kx->retry_set_key_task);
+    kx->retry_set_key_task = GNUNET_SCHEDULER_NO_TASK;
+  }  
   switch (kx->status)
   {
   case KX_STATE_DOWN:
-    GNUNET_break (0);           /* should be impossible */
+    GNUNET_assert (0);           /* should be impossible */
     return;
   case KX_STATE_KEY_SENT:
-    GNUNET_break (0);           /* should be impossible */
+    GNUNET_assert (0);           /* should be impossible */
     return;
   case KX_STATE_KEY_RECEIVED:
     GNUNET_STATISTICS_update (GSC_stats,
@@ -1304,32 +1108,22 @@
                               GNUNET_NO);
     kx->status = KX_STATE_UP;
     GSC_SESSIONS_create (&kx->peer, kx);
-    CHECK_KX (kx);
-    schedule_rekey (kx);
     GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == kx->keep_alive_task);
-    if (NULL != kx->emsg_received)
-    {
-      emsg = kx->emsg_received;
-      kx->emsg_received = NULL;
-      GSC_KX_handle_encrypted_message (kx, &emsg->header, NULL,
-                                       0 /* FIXME: ATSI */ );
-      GNUNET_free (emsg);
-    }
     update_timeout (kx);
     break;
   case KX_STATE_UP:
+    GNUNET_STATISTICS_update (GSC_stats,
+                              gettext_noop
+                              ("# timeouts prevented via PONG"), 1,
+                              GNUNET_NO);
     update_timeout (kx);
     break;
-  case KX_STATE_REKEY:
-    update_timeout (kx);
-    break;
   case KX_STATE_REKEY_SENT:
     GNUNET_STATISTICS_update (GSC_stats,
                               gettext_noop
                               ("# rekey operations confirmed via PONG"), 1,
                               GNUNET_NO);
     kx->status = KX_STATE_UP;
-    schedule_rekey (kx);
     update_timeout (kx);
     break;
   default:
@@ -1340,87 +1134,26 @@
 
 
 /**
- * Send our key (and encrypted PING) to the other peer.
+ * Send our key to the other peer.
  *
  * @param kx key exchange context
  */
 static void
 send_key (struct GSC_KeyExchangeInfo *kx)
 {
-  CHECK_KX (kx);
   if (GNUNET_SCHEDULER_NO_TASK != kx->retry_set_key_task)
   {
      GNUNET_SCHEDULER_cancel (kx->retry_set_key_task);
      kx->retry_set_key_task = GNUNET_SCHEDULER_NO_TASK;
   }
-  if (KX_STATE_UP == kx->status)
-    return;                     /* nothing to do */
-  if (NULL == kx->public_key)
-  {
-    if (NULL != kx->pitr)
-      return;
-    /* lookup public key, then try again */
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Trying to obtain public key for `%s'\n",
-                GNUNET_i2s (&kx->peer));
-    kx->pitr =
-        GNUNET_PEERINFO_iterate (peerinfo, &kx->peer,
-                                 GNUNET_TIME_UNIT_FOREVER_REL /* timeout? */ ,
-                                 &process_hello, kx);
-    return;
-  }
-
-  /* update status */
-  switch (kx->status)
-  {
-  case KX_STATE_DOWN:
-    kx->status = KX_STATE_KEY_SENT;
-    /* setup SET KEY message */
-    setup_fresh_setkey (kx);
-    setup_fresh_ping (kx);
-    GNUNET_STATISTICS_update (GSC_stats,
-                              gettext_noop
-                              ("# SET_KEY and PING messages created"), 1,
-                              GNUNET_NO);
-    break;
-  case KX_STATE_KEY_SENT:
-    break;
-  case KX_STATE_KEY_RECEIVED:
-    break;
-  case KX_STATE_UP:
-    GNUNET_break (0);
-    return;
-  case KX_STATE_REKEY:
-    kx->status = KX_STATE_REKEY_SENT;
-    /* setup fresh SET KEY message */
-    setup_fresh_setkey (kx);
-    setup_fresh_ping (kx);
-    GNUNET_STATISTICS_update (GSC_stats,
-                              gettext_noop
-                              ("# SET_KEY and PING messages created"), 1,
-                              GNUNET_NO);
-    GNUNET_STATISTICS_update (GSC_stats,
-                              gettext_noop
-                              ("# REKEY operations performed"), 1,
-                              GNUNET_NO);
-    break;
-  case KX_STATE_REKEY_SENT:
-    break;
-  default:
-    GNUNET_break (0);
-    return;
-  }
-
   /* always update sender status in SET KEY message */
-  /* Not sending rekey sent state to be compatible with GNUnet 0.9.2 */
-  kx->skm.sender_status = htonl ((int32_t) ((kx->status == KX_STATE_REKEY_SENT) ? 
-					    KX_STATE_KEY_RECEIVED : kx->status));  
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending SET_KEY and PING to `%s'\n",
-              GNUNET_i2s (&kx->peer));
-  GSC_NEIGHBOURS_transmit (&kx->peer, &kx->skm.header,
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
+	      "Sending key to `%s' (my status: %d)\n",
+              GNUNET_i2s (&kx->peer),
+	      kx->status);
+  current_ekm.sender_status = htonl ((int32_t) (kx->status));  
+  GSC_NEIGHBOURS_transmit (&kx->peer, &current_ekm.header,
                            kx->set_key_retry_frequency);
-  GSC_NEIGHBOURS_transmit (&kx->peer, &kx->ping.header,
-                           kx->set_key_retry_frequency);
   kx->retry_set_key_task =
       GNUNET_SCHEDULER_add_delayed (kx->set_key_retry_frequency,
                                     &set_key_retry_task, kx);
@@ -1451,7 +1184,7 @@
       htonl (GNUNET_CRYPTO_random_u32
              (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX));
   ph->sequence_number = htonl (++kx->last_sequence_number_sent);
-  ph->reserved = GNUNET_BANDWIDTH_VALUE_MAX;
+  ph->reserved = 0;
   ph->timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
   memcpy (&ph[1], payload, payload_size);
 
@@ -1466,8 +1199,9 @@
                              used - ENCRYPTED_HEADER_SIZE));
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Encrypted %u bytes for %s\n",
               used - ENCRYPTED_HEADER_SIZE, GNUNET_i2s (&kx->peer));
-  derive_auth_key (&auth_key, &kx->encrypt_key, ph->iv_seed,
-                   kx->encrypt_key_created);
+  derive_auth_key (&auth_key, 
+		   &kx->encrypt_key, 
+		   ph->iv_seed);
   GNUNET_CRYPTO_hmac (&auth_key, &em->sequence_number,
                       used - ENCRYPTED_HEADER_SIZE, &em->hmac);
   GSC_NEIGHBOURS_transmit (&kx->peer, &em->header,
@@ -1531,31 +1265,35 @@
     return;
   }
   m = (const struct EncryptedMessage *) msg;
-  if ((kx->status != KX_STATE_KEY_RECEIVED) && (kx->status != KX_STATE_UP) &&
-      (kx->status != KX_STATE_REKEY_SENT) )
+  if (kx->status != KX_STATE_UP)
   {
     GNUNET_STATISTICS_update (GSC_stats,
                               gettext_noop
-                              ("# failed to decrypt message (no session key)"),
+                              ("# DATA message dropped (out of order)"),
                               1, GNUNET_NO);
     return;
   }
-  if (KX_STATE_KEY_RECEIVED == kx->status)
+  if (0 == GNUNET_TIME_absolute_get_remaining (kx->foreign_key_expires).rel_value)
   {
-    /* defer */
-    GNUNET_free_non_null (kx->emsg_received);
-    kx->emsg_received = (struct EncryptedMessage *) GNUNET_copy_message (msg);
+    kx->status = KX_STATE_KEY_SENT;
+    GNUNET_STATISTICS_update (GSC_stats,
+                              gettext_noop
+                              ("# DATA message dropped (session key expired)"),
+                              1, GNUNET_NO);
+    send_key (kx);
     return;
   }
+
   /* validate hash */
-  derive_auth_key (&auth_key, &kx->decrypt_key, m->iv_seed,
-                   kx->decrypt_key_created);
+  derive_auth_key (&auth_key, &kx->decrypt_key, m->iv_seed);
   GNUNET_CRYPTO_hmac (&auth_key, &m->sequence_number,
                       size - ENCRYPTED_HEADER_SIZE, &ph);
   if (0 != memcmp (&ph, &m->hmac, sizeof (struct GNUNET_HashCode)))
   {
     /* checksum failed */
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed checksum validation for a message from `%s'\n", GNUNET_i2s (&kx->peer));
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
+		"Failed checksum validation for a message from `%s'\n", 
+		GNUNET_i2s (&kx->peer));
     return;
   }
   derive_iv (&iv, &kx->decrypt_key, m->iv_seed, &GSC_my_identity);
@@ -1572,7 +1310,7 @@
   snum = ntohl (pt->sequence_number);
   if (kx->last_sequence_number_received == snum)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                 "Received duplicate message, ignoring.\n");
     /* duplicate, ignore */
     GNUNET_STATISTICS_update (GSC_stats,
@@ -1583,7 +1321,7 @@
   if ((kx->last_sequence_number_received > snum) &&
       (kx->last_sequence_number_received - snum > 32))
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                 "Received ancient out of sequence message, ignoring.\n");
     /* ancient out of sequence, ignore */
     GNUNET_STATISTICS_update (GSC_stats,
@@ -1598,7 +1336,7 @@
 
     if ((kx->last_packets_bitmap & rotbit) != 0)
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                   "Received duplicate message, ignoring.\n");
       GNUNET_STATISTICS_update (GSC_stats,
                                 gettext_noop ("# bytes dropped (duplicates)"),
@@ -1624,8 +1362,8 @@
   if (GNUNET_TIME_absolute_get_duration (t).rel_value >
       MAX_MESSAGE_AGE.rel_value)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                _("Message received far too old (%s). Content ignored.\n"),
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Message received far too old (%s). Content ignored.\n",
                 GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (t), GNUNET_YES));
     GNUNET_STATISTICS_update (GSC_stats,
                               gettext_noop
@@ -1684,28 +1422,89 @@
 
 
 /**
+ * Setup the message that links the ephemeral key to our persistent
+ * public key and generate the appropriate signature.
+ */
+static void
+sign_ephemeral_key ()
+{
+  current_ekm.header.size = htons (sizeof (struct EphemeralKeyMessage));
+  current_ekm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_EPHEMERAL_KEY);
+  current_ekm.sender_status = 0; /* to be set later */
+  current_ekm.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_SET_ECC_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));
+  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)));
+  GNUNET_CRYPTO_ecc_key_get_public (my_ephemeral_key,
+				    &current_ekm.ephemeral_key);
+  current_ekm.origin_public_key = my_public_key;
+  GNUNET_assert (GNUNET_OK ==
+		 GNUNET_CRYPTO_ecc_sign (my_private_key,
+					 &current_ekm.purpose,
+					 &current_ekm.signature));
+}
+
+
+/**
+ * Task run to trigger rekeying.
+ *
+ * @param cls closure, NULL
+ * @param tc scheduler context
+ */
+static void
+do_rekey (void *cls,
+	  const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct GSC_KeyExchangeInfo *pos;
+
+  rekey_task = GNUNET_SCHEDULER_add_delayed (REKEY_FREQUENCY,
+					     &do_rekey,
+					     NULL);
+  if (NULL != my_ephemeral_key)
+    GNUNET_CRYPTO_ecc_key_free (my_ephemeral_key);
+  my_ephemeral_key = GNUNET_CRYPTO_ecc_key_create ();
+  GNUNET_assert (NULL != my_ephemeral_key);
+  sign_ephemeral_key ();
+  for (pos = kx_head; NULL != pos; pos = pos->next)
+  {
+    pos->status = KX_STATE_REKEY_SENT;
+    send_key (pos);
+  }
+}
+
+
+/**
  * Initialize KX subsystem.
  *
  * @param pk private key to use for the peer
  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
  */
 int
-GSC_KX_init (struct GNUNET_CRYPTO_RsaPrivateKey *pk)
+GSC_KX_init (struct GNUNET_CRYPTO_EccPrivateKey *pk)
 {
+  GNUNET_assert (NULL != pk);
   my_private_key = pk;
-  GNUNET_CRYPTO_rsa_key_get_public (my_private_key, &my_public_key);
+  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);
-  peerinfo = GNUNET_PEERINFO_connect (GSC_cfg);
-  if (NULL == peerinfo)
+  my_ephemeral_key = GNUNET_CRYPTO_ecc_key_create ();
+  if (NULL == my_ephemeral_key)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                _("Could not access PEERINFO service.  Exiting.\n"));
-    GNUNET_CRYPTO_rsa_key_free (my_private_key);
+    GNUNET_break (0);
+    GNUNET_CRYPTO_ecc_key_free (my_private_key);
     my_private_key = NULL;
     return GNUNET_SYSERR;
   }
+  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;
 }
 
@@ -1716,15 +1515,20 @@
 void
 GSC_KX_done ()
 {
+  if (GNUNET_SCHEDULER_NO_TASK != rekey_task)
+  {
+    GNUNET_SCHEDULER_cancel (rekey_task);
+    rekey_task = GNUNET_SCHEDULER_NO_TASK;
+  }
   if (NULL != my_private_key)
   {
-    GNUNET_CRYPTO_rsa_key_free (my_private_key);
+    GNUNET_CRYPTO_ecc_key_free (my_private_key);
     my_private_key = NULL;
   }
-  if (NULL != peerinfo)
+  if (NULL != my_ephemeral_key)
   {
-    GNUNET_PEERINFO_disconnect (peerinfo);
-    peerinfo = NULL;
+    GNUNET_CRYPTO_ecc_key_free (my_ephemeral_key);
+    my_ephemeral_key = NULL;
   }
   if (NULL != mst)
   {
Index: src/core/gnunet-service-core_kx.h
===================================================================
--- src/core/gnunet-service-core_kx.h	(revision 26008)
+++ src/core/gnunet-service-core_kx.h	(working copy)
@@ -37,15 +37,15 @@
 
 
 /**
- * We received a SET_KEY message.  Validate and update
+ * We received a EPHEMERAL_KEY message.  Validate and update
  * our key material and status.
  *
  * @param kx key exchange status for the corresponding peer
  * @param msg the set key message we received
  */
 void
-GSC_KX_handle_set_key (struct GSC_KeyExchangeInfo *kx,
-                       const struct GNUNET_MessageHeader *msg);
+GSC_KX_handle_ephemeral_key (struct GSC_KeyExchangeInfo *kx,
+			     const struct GNUNET_MessageHeader *msg);
 
 
 /**
@@ -125,7 +125,7 @@
  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
  */
 int
-GSC_KX_init (struct GNUNET_CRYPTO_RsaPrivateKey *pk);
+GSC_KX_init (struct GNUNET_CRYPTO_EccPrivateKey *pk);
 
 
 /**
Index: src/core/gnunet-service-core.c
===================================================================
--- src/core/gnunet-service-core.c	(revision 26008)
+++ src/core/gnunet-service-core.c	(working copy)
@@ -56,7 +56,7 @@
 /**
  * Hostkey generation context
  */
-static struct GNUNET_CRYPTO_RsaKeyGenerationContext *keygen;
+static struct GNUNET_CRYPTO_EccKeyGenerationContext *keygen;
 
 
 /**
@@ -72,7 +72,7 @@
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Core service shutting down.\n");
   if (NULL != keygen)
   {
-    GNUNET_CRYPTO_rsa_key_create_stop (keygen);
+    GNUNET_CRYPTO_ecc_key_create_stop (keygen);
     keygen = NULL;
   }
   GSC_CLIENTS_done ();
@@ -99,14 +99,14 @@
  */
 static void
 key_generation_cb (void *cls,
-                   struct GNUNET_CRYPTO_RsaPrivateKey *pk,
+                   struct GNUNET_CRYPTO_EccPrivateKey *pk,
                    const char *emsg)
 {
   keygen = NULL;
   if (NULL == pk)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-		_("Failed to read hostkey: %s\n"),
+		_("Failed to read or generate private key: %s\n"),
 		emsg);
     GNUNET_SCHEDULER_shutdown ();
     return;
@@ -141,7 +141,7 @@
   GSC_cfg = c;
   GSC_server = server;
   if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_filename (GSC_cfg, "GNUNETD", "HOSTKEY",
+      GNUNET_CONFIGURATION_get_value_filename (GSC_cfg, "PEER", "PRIVATE_KEY",
                                                &keyfile))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -155,7 +155,7 @@
                                 NULL);
   GNUNET_SERVER_suspend (server);
   GSC_TYPEMAP_init ();
-  keygen = GNUNET_CRYPTO_rsa_key_create_start (keyfile, &key_generation_cb, NULL);
+  keygen = GNUNET_CRYPTO_ecc_key_create_start (keyfile, &key_generation_cb, NULL);
   GNUNET_free (keyfile);
   if (NULL == keygen)
   {
Index: src/core/gnunet-service-core_neighbours.c
===================================================================
--- src/core/gnunet-service-core_neighbours.c	(revision 26008)
+++ src/core/gnunet-service-core_neighbours.c	(working copy)
@@ -400,8 +400,8 @@
   type = ntohs (message->type);
   switch (type)
   {
-  case GNUNET_MESSAGE_TYPE_CORE_SET_KEY:
-    GSC_KX_handle_set_key (n->kxinfo, message);
+  case GNUNET_MESSAGE_TYPE_CORE_EPHEMERAL_KEY:
+    GSC_KX_handle_ephemeral_key (n->kxinfo, message);
     break;
   case GNUNET_MESSAGE_TYPE_CORE_PING:
     GSC_KX_handle_ping (n->kxinfo, message);
Index: src/hello/gnunet-hello.c
===================================================================
--- src/hello/gnunet-hello.c	(revision 26008)
+++ src/hello/gnunet-hello.c	(working copy)
@@ -107,7 +107,7 @@
   struct GNUNET_DISK_FileHandle *fh;
   struct GNUNET_HELLO_Message *orig;
   struct GNUNET_HELLO_Message *result;
-  struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pk;
+  struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded pk;
   uint64_t fsize;
 
   GNUNET_log_setup ("gnunet-hello", "INFO", NULL);
Index: src/hello/test_hello.c
===================================================================
--- src/hello/test_hello.c	(revision 26008)
+++ src/hello/test_hello.c	(working copy)
@@ -93,8 +93,8 @@
   struct GNUNET_HELLO_Message *msg1;
   struct GNUNET_HELLO_Message *msg2;
   struct GNUNET_HELLO_Message *msg3;
-  struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded publicKey;
-  struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pk;
+  struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded publicKey;
+  struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded pk;
   struct GNUNET_TIME_Absolute startup_time;
   unsigned int i;
 
Index: src/hello/hello.c
===================================================================
--- src/hello/hello.c	(revision 26008)
+++ src/hello/hello.c	(working copy)
@@ -59,7 +59,7 @@
   /**
    * The public key of the peer.
    */
-  struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded publicKey;
+  struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded publicKey;
 
 };
 GNUNET_NETWORK_STRUCT_END
@@ -201,7 +201,7 @@
  * @return the hello message
  */
 struct GNUNET_HELLO_Message *
-GNUNET_HELLO_create (const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
+GNUNET_HELLO_create (const struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded
                      *publicKey,
                      GNUNET_HELLO_GenerateAddressListCallback addrgen,
                      void *addrgen_cls)
@@ -227,7 +227,7 @@
   hello->header.type = htons (GNUNET_MESSAGE_TYPE_HELLO);
   hello->header.size = htons (sizeof (struct GNUNET_HELLO_Message) + used);
   memcpy (&hello->publicKey, publicKey,
-          sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
+          sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded));
   memcpy (&hello[1], buffer, used);
   return hello;
 }
@@ -274,7 +274,7 @@
   wpos = 0;
   woff = (ret != NULL) ? (char *) &ret[1] : NULL;
   GNUNET_CRYPTO_hash (&msg->publicKey,
-                      sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
+                      sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded),
                       &address.peer.hashPubKey);
   while (insize > 0)
   {
@@ -503,7 +503,7 @@
  */
 int
 GNUNET_HELLO_get_key (const struct GNUNET_HELLO_Message *hello,
-                      struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey)
+                      struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *publicKey)
 {
   uint16_t ret = ntohs (hello->header.size);
 
@@ -532,7 +532,7 @@
       (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
     return GNUNET_SYSERR;
   GNUNET_CRYPTO_hash (&hello->publicKey,
-                      sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
+                      sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded),
                       &peer->hashPubKey);
   return GNUNET_OK;
 }
@@ -641,7 +641,7 @@
 
   if (0 !=
       memcmp (&h1->publicKey, &h2->publicKey,
-              sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)))
+              sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded)))
     return GNUNET_TIME_UNIT_ZERO_ABS;
   ec.expiration_limit = now;
   ec.result = GNUNET_TIME_UNIT_FOREVER_ABS;
@@ -833,7 +833,7 @@
   struct GNUNET_HELLO_ComposeUriContext ctx;
   ctx.plugins_find = plugins_find;
 
-  char *pkey = GNUNET_CRYPTO_rsa_public_key_to_string (&(hello->publicKey));
+  char *pkey = GNUNET_CRYPTO_ecc_public_key_to_string (&(hello->publicKey));
   GNUNET_asprintf (&(ctx.uri),
                    "%s%s",
                    GNUNET_HELLO_URI_PREFIX,
@@ -1005,7 +1005,7 @@
  */
 int
 GNUNET_HELLO_parse_uri (const char *uri,
-                        struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *pubkey,
+                        struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *pubkey,
                         struct GNUNET_HELLO_Message **hello,
                         GNUNET_HELLO_TransportPluginsFind plugins_find)
 {
Index: src/transport/gnunet-service-transport_hello.c
===================================================================
--- src/transport/gnunet-service-transport_hello.c	(revision 26008)
+++ src/transport/gnunet-service-transport_hello.c	(working copy)
@@ -74,7 +74,7 @@
   /**
    * Signature for a 'struct TransportPongMessage' for this address.
    */
-  struct GNUNET_CRYPTO_RsaSignature pong_signature;
+  struct GNUNET_CRYPTO_EccSignature pong_signature;
 
 };
 
@@ -299,7 +299,7 @@
  */
 int
 GST_hello_test_address (const struct GNUNET_HELLO_Address *address,
-                        struct GNUNET_CRYPTO_RsaSignature **sig,
+                        struct GNUNET_CRYPTO_EccSignature **sig,
                         struct GNUNET_TIME_Absolute **sig_expiration)
 {
   struct OwnAddressList *al;
Index: src/transport/gnunet-service-transport_hello.h
===================================================================
--- src/transport/gnunet-service-transport_hello.h	(revision 26008)
+++ src/transport/gnunet-service-transport_hello.h	(working copy)
@@ -92,7 +92,7 @@
  */
 int
 GST_hello_test_address (const struct GNUNET_HELLO_Address *address,
-                        struct GNUNET_CRYPTO_RsaSignature **sig,
+                        struct GNUNET_CRYPTO_EccSignature **sig,
                         struct GNUNET_TIME_Absolute **sig_expiration);
 
 
Index: src/transport/gnunet-service-transport.c
===================================================================
--- src/transport/gnunet-service-transport.c	(revision 26008)
+++ src/transport/gnunet-service-transport.c	(working copy)
@@ -64,7 +64,7 @@
 /**
  * Hostkey generation context
  */
-struct GNUNET_CRYPTO_RsaKeyGenerationContext *GST_keygen;
+struct GNUNET_CRYPTO_EccKeyGenerationContext *GST_keygen;
 
 /**
  * Handle to our service's server.
@@ -74,12 +74,12 @@
 /**
  * Our public key.
  */
-struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded GST_my_public_key;
+struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded GST_my_public_key;
 
 /**
  * Our private key.
  */
-struct GNUNET_CRYPTO_RsaPrivateKey *GST_my_private_key;
+struct GNUNET_CRYPTO_EccPrivateKey *GST_my_private_key;
 
 /**
  * ATS handle.
@@ -556,7 +556,7 @@
 {
   if (NULL != GST_keygen)
   {
-    GNUNET_CRYPTO_rsa_key_create_stop (GST_keygen);
+    GNUNET_CRYPTO_ecc_key_create_stop (GST_keygen);
     GST_keygen = NULL;
   }
   GST_neighbours_stop ();
@@ -581,7 +581,7 @@
   }
   if (NULL != GST_my_private_key)
   {
-    GNUNET_CRYPTO_rsa_key_free (GST_my_private_key);
+    GNUNET_CRYPTO_ecc_key_free (GST_my_private_key);
     GST_my_private_key = NULL;
   }
   GST_server = NULL;
@@ -597,10 +597,9 @@
  */
 static void
 key_generation_cb (void *cls,
-                   struct GNUNET_CRYPTO_RsaPrivateKey *pk,
+                   struct GNUNET_CRYPTO_EccPrivateKey *pk,
                    const char *emsg)
 {
-  struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded tmp;
   long long unsigned int max_fd_cfg;
   int max_fd_rlimit;
   int max_fd;
@@ -625,14 +624,10 @@
     return;
   }
   GST_peerinfo = GNUNET_PEERINFO_connect (GST_cfg);
-  memset (&GST_my_public_key, '\0', sizeof (GST_my_public_key));
-  memset (&tmp, '\0', sizeof (tmp));
-  GNUNET_CRYPTO_rsa_key_get_public (GST_my_private_key, &GST_my_public_key);
+  GNUNET_CRYPTO_ecc_key_get_public (GST_my_private_key, &GST_my_public_key);
   GNUNET_CRYPTO_hash (&GST_my_public_key, sizeof (GST_my_public_key),
                       &GST_my_identity.hashPubKey);
-
   GNUNET_assert (NULL != GST_my_private_key);
-  GNUNET_assert (0 != memcmp (&GST_my_public_key, &tmp, sizeof (GST_my_public_key)));
 
   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
                                 NULL);
@@ -709,7 +704,7 @@
   /* setup globals */
   GST_cfg = c;
   if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_filename (c, "GNUNETD", "HOSTKEY",
+      GNUNET_CONFIGURATION_get_value_filename (c, "PEER", "PRIVATE_KEY",
                                                &keyfile))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -726,7 +721,7 @@
   }
   GST_server = server;
   GNUNET_SERVER_suspend (server);
-  GST_keygen = GNUNET_CRYPTO_rsa_key_create_start (keyfile, &key_generation_cb, NULL);
+  GST_keygen = GNUNET_CRYPTO_ecc_key_create_start (keyfile, &key_generation_cb, NULL);
   GNUNET_free (keyfile);
   if (NULL == GST_keygen)
   {
Index: src/transport/gnunet-service-transport.h
===================================================================
--- src/transport/gnunet-service-transport.h	(revision 26008)
+++ src/transport/gnunet-service-transport.h	(working copy)
@@ -55,12 +55,12 @@
 /**
  * Our public key.
  */
-extern struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded GST_my_public_key;
+extern struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded GST_my_public_key;
 
 /**
  * Our private key.
  */
-extern struct GNUNET_CRYPTO_RsaPrivateKey *GST_my_private_key;
+extern struct GNUNET_CRYPTO_EccPrivateKey *GST_my_private_key;
 
 /**
  * ATS handle.
Index: src/transport/gnunet-service-transport_validation.c
===================================================================
--- src/transport/gnunet-service-transport_validation.c	(revision 26008)
+++ src/transport/gnunet-service-transport_validation.c	(working copy)
@@ -149,13 +149,13 @@
   /**
    * Signature.
    */
-  struct GNUNET_CRYPTO_RsaSignature signature;
+  struct GNUNET_CRYPTO_EccSignature signature;
 
   /**
    * GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN to confirm that this is a
    * plausible address for the signing peer.
    */
-  struct GNUNET_CRYPTO_RsaSignaturePurpose purpose;
+  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
 
   /**
    * When does this signature expire?
@@ -190,7 +190,7 @@
   /**
    * Public key of the peer.
    */
-  struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
+  struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded public_key;
 
   /**
    * The identity of the peer. FIXME: duplicated (also in 'address')
@@ -636,7 +636,7 @@
  *         if we don't have an existing entry and no public key was given
  */
 static struct ValidationEntry *
-find_validation_entry (const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
+find_validation_entry (const struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded
                        *public_key, const struct GNUNET_HELLO_Address *address)
 {
   struct ValidationEntryMatchContext vemc;
@@ -688,7 +688,7 @@
   const struct GNUNET_HELLO_Message *hello = cls;
   struct ValidationEntry *ve;
   struct GNUNET_PeerIdentity pid;
-  struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
+  struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded public_key;
 
   if (GNUNET_TIME_absolute_get_remaining (expiration).rel_value == 0)
     return GNUNET_OK;           /* expired */
@@ -789,7 +789,7 @@
  */
 static void
 multicast_pong (void *cls,
-                const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
+                const struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded
                 *public_key, struct GNUNET_TIME_Absolute valid_until,
                 struct GNUNET_TIME_Absolute validation_block,
                 const struct GNUNET_HELLO_Address *address)
@@ -835,7 +835,7 @@
   const struct TransportPingMessage *ping;
   struct TransportPongMessage *pong;
   struct GNUNET_TRANSPORT_PluginFunctions *papi;
-  struct GNUNET_CRYPTO_RsaSignature *sig_cache;
+  struct GNUNET_CRYPTO_EccSignature *sig_cache;
   struct GNUNET_TIME_Absolute *sig_cache_exp;
   const char *addr;
   const char *addrend;
@@ -931,7 +931,7 @@
   {
     addrend = NULL;             /* make gcc happy */
     slen = 0;
-    static struct GNUNET_CRYPTO_RsaSignature no_address_signature;
+    static struct GNUNET_CRYPTO_EccSignature no_address_signature;
     static struct GNUNET_TIME_Absolute no_address_signature_expiration;
 
     sig_cache = &no_address_signature;
@@ -951,7 +951,7 @@
       htons (sizeof (struct TransportPongMessage) + alen + slen);
   pong->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PONG);
   pong->purpose.size =
-      htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) +
+      htonl (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
              sizeof (uint32_t) + sizeof (struct GNUNET_TIME_AbsoluteNBO) +
              alen + slen);
   pong->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN);
@@ -981,7 +981,7 @@
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating buggy PONG signature to indicate ownership.\n");
     pong->expiration = GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (PONG_SIGNATURE_LIFETIME));
     GNUNET_assert (GNUNET_OK ==
-                   GNUNET_CRYPTO_rsa_sign (GST_my_private_key, &pong->purpose,
+                   GNUNET_CRYPTO_ecc_sign (GST_my_private_key, &pong->purpose,
                                            &pong->signature));
   }
   else
@@ -1001,7 +1001,7 @@
       *sig_cache_exp = GNUNET_TIME_relative_to_absolute (PONG_SIGNATURE_LIFETIME);
       pong->expiration = GNUNET_TIME_absolute_hton (*sig_cache_exp);
       GNUNET_assert (GNUNET_OK ==
-                     GNUNET_CRYPTO_rsa_sign (GST_my_private_key, &pong->purpose,
+                     GNUNET_CRYPTO_ecc_sign (GST_my_private_key, &pong->purpose,
                                              sig_cache));
     }
     else
@@ -1080,7 +1080,7 @@
   /**
    * Public key of the peer whose address is being validated.
    */
-  struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
+  struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded public_key;
 };
 
 
@@ -1197,7 +1197,7 @@
   }
 
   if (GNUNET_OK !=
-      GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN,
+      GNUNET_CRYPTO_ecc_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN,
                                 &pong->purpose, &pong->signature,
                                 &ve->public_key))
   {
Index: src/transport/gnunet-service-transport_validation.h
===================================================================
--- src/transport/gnunet-service-transport_validation.h	(revision 26008)
+++ src/transport/gnunet-service-transport_validation.h	(working copy)
@@ -134,7 +134,7 @@
  */
 typedef void (*GST_ValidationAddressCallback) (void *cls,
                                                const struct
-                                               GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
+                                               GNUNET_CRYPTO_EccPublicKeyBinaryEncoded
                                                * public_key,
                                                struct GNUNET_TIME_Absolute
                                                valid_until,
Index: src/transport/gnunet-service-transport_neighbours.c
===================================================================
--- src/transport/gnunet-service-transport_neighbours.c	(revision 26008)
+++ src/transport/gnunet-service-transport_neighbours.c	(working copy)
@@ -318,7 +318,7 @@
    * Purpose of the signature.  Extends over the timestamp.
    * Purpose should be GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DISCONNECT.
    */
-  struct GNUNET_CRYPTO_RsaSignaturePurpose purpose;
+  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
 
   /**
    * Absolute time at the sender.  Only the most recent connect
@@ -329,14 +329,14 @@
   /**
    * Public key of the sender.
    */
-  struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
+  struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded public_key;
 
   /**
    * Signature of the peer that sends us the disconnect.  Only
    * valid if the timestamp is AFTER the timestamp from the
    * corresponding 'CONNECT' message.
    */
-  struct GNUNET_CRYPTO_RsaSignature signature;
+  struct GNUNET_CRYPTO_EccSignature signature;
 
 };
 
@@ -1205,8 +1205,8 @@
       htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT);
   disconnect_msg.reserved = htonl (0);
   disconnect_msg.purpose.size =
-      htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) +
-             sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) +
+      htonl (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
+             sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded) +
              sizeof (struct GNUNET_TIME_AbsoluteNBO));
   disconnect_msg.purpose.purpose =
       htonl (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT);
@@ -1214,7 +1214,7 @@
       GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
   disconnect_msg.public_key = GST_my_public_key;
   GNUNET_assert (GNUNET_OK ==
-                 GNUNET_CRYPTO_rsa_sign (GST_my_private_key,
+                 GNUNET_CRYPTO_ecc_sign (GST_my_private_key,
                                          &disconnect_msg.purpose,
                                          &disconnect_msg.signature));
 
@@ -3195,7 +3195,7 @@
     return;
   }
   GNUNET_CRYPTO_hash (&sdm->public_key,
-                      sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
+                      sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded),
                       &hc);
   if (0 != memcmp (peer, &hc, sizeof (struct GNUNET_PeerIdentity)))
   {
@@ -3203,15 +3203,15 @@
     return;
   }
   if (ntohl (sdm->purpose.size) !=
-      sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) +
-      sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) +
+      sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
+      sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded) +
       sizeof (struct GNUNET_TIME_AbsoluteNBO))
   {
     GNUNET_break_op (0);
     return;
   }
   if (GNUNET_OK !=
-      GNUNET_CRYPTO_rsa_verify
+      GNUNET_CRYPTO_ecc_verify
       (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT, &sdm->purpose,
        &sdm->signature, &sdm->public_key))
   {
Index: src/transport/test_plugin_transport.c
===================================================================
--- src/transport/test_plugin_transport.c	(revision 26008)
+++ src/transport/test_plugin_transport.c	(working copy)
@@ -43,12 +43,12 @@
 #define WAIT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
 
-#define HOSTKEY_FILE "test_plugin_hostkey"
+#define HOSTKEY_FILE "test_plugin_hostkey.ecc"
 
 /**
  * Our public key.
  */
-static struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key;
+static struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded my_public_key;
 
 /**
  * Our identity.
@@ -58,7 +58,7 @@
 /**
  * Our private key.
  */
-static struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key;
+static struct GNUNET_CRYPTO_EccPrivateKey *my_private_key;
 
 /**
  * Our configuration.
@@ -533,7 +533,7 @@
 							    "NEIGHBOUR_LIMIT",
 							    &tneigh)) ||
        (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (c,
-							      "GNUNETD", "HOSTKEY",
+							      "PEER", "PRIVATE_KEY",
 							      &keyfile)))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -576,7 +576,7 @@
 
 
   max_connect_per_transport = (uint32_t) tneigh;
-  my_private_key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
+  my_private_key = GNUNET_CRYPTO_ecc_key_create_from_file (keyfile);
   GNUNET_free (keyfile);
   if (NULL == my_private_key)
   {
@@ -585,7 +585,7 @@
     end_badly_now ();
     return;
   }
-  GNUNET_CRYPTO_rsa_key_get_public (my_private_key, &my_public_key);
+  GNUNET_CRYPTO_ecc_key_get_public (my_private_key, &my_public_key);
   GNUNET_CRYPTO_hash (&my_public_key, sizeof (my_public_key),
                       &my_identity.hashPubKey);
 
Index: README
===================================================================
--- README	(revision 26008)
+++ README	(working copy)
@@ -1,3 +1,16 @@
+======================================================================
+
+THIS SVN VERSION OF GNUNET IS INCOMPATIBLE TO ALL PREVIOUS RELEASES.
+WE ARE WORKING ON CHANGING FUNDAMENTAL PARTS OF THE CRYPTOGRAPHIC
+PARTS OF THE SYSTEM.  
+
+THE CURRENT IMPLEMENTATION DOES NOT GIVE YOU ANY SECURITY (AS SOME
+CRYPTO IS DISABLED/INCOMPLETE) AND WILL LIKELY NOT BE COMPATIBLE WITH
+YESTERDAYS OR TOMORROWS VERSION.  AS SUCH, DO NOT USE IT, OTHER THAN
+FOR DEVELOPMENT!
+
+=======================================================================
+
                        Welcome to GNUnet
 
 
combined.diff (95,019 bytes)   

Relationships

related to 0002566 closedChristian Grothoff need abstractions for DH crypto in util 
related to 0002321 closedChristian Grothoff External protocol violation detected at gnunet-service-core_kx.c:1555. 

Activities

Christian Grothoff

2012-10-12 12:38

manager   ~0006436

Last edited: 2012-10-12 13:21

DH with ECC might also be good, especially since libgcrypt supports ECC these days (http://www.gnupg.org/documentation/manuals/gcrypt/ECC-key-parameters.html#ECC-key-parameters). So overall, I'm thinking of using Station-to-Station (http://en.wikipedia.org/wiki/Station-to-Station_protocol), so DH-based key exchange (2048-bit p) signed with ECC crypto (NIST P-521) to create an ephemeral key. Either peer can at any time re-create their ephemeral random input from the DH-exchange to rekey.

As we want a pair of keys for the symmetric encryption, I'm thinking of then applying HKDF on the DH-derived key to get encryption keys Alice->Bob, Bob->Alice.

Furthermore, we should again have a 'rekey-request' bit so that Bob can ask Alice to (re)send a DH-KX message in case Bob crashed and Alice still thinks she has a valid session (otherwise Bob may send a fresh key but might not learn Alices "old" key in a timely fashion). Again, we have this already in the current protocol as the 'status' enum.

Naturally, timestamps would also continue to be included in the KX (as they are right now) to prevent any kind of replay (even though such a replay is much less dangerous given ephemeral keys).

Christian Grothoff

2013-02-05 12:53

manager   ~0006836

Added preview-patch for moving to ECC to the hello/peerinfo/testing/transport subsystems.

Christian Grothoff

2013-02-05 12:55

manager   ~0006837

Core patch is now also attached; untested, and will BREAK key generation, so all peers will use the same symmetric key derived from all-zeros. Work-in-progress!

Christian Grothoff

2013-02-05 21:49

manager   ~0006840

Partial patch, with one tiny bit of support missing in libgcrypt to finish it, is now in SVN 26035. This TOTALLY breaks security of the system as currently all peers use the same session key secret!

Christian Grothoff

2013-03-04 03:53

manager   ~0006912

We're waiting for a resolution of https://bugs.g10code.com/gnupg/issue1470

Christian Grothoff

2013-03-20 13:53

manager   ~0006995

Fixed in SVN 26516.

Issue History

Date Modified Username Field Change
2012-09-27 20:15 Christian Grothoff New Issue
2012-09-27 20:17 Christian Grothoff Status new => confirmed
2012-09-27 20:22 Christian Grothoff Relationship added related to 0002566
2012-09-27 20:32 Christian Grothoff Target Version => 0.10.0
2012-09-29 21:20 Christian Grothoff Relationship added related to 0002321
2012-10-07 14:19 Christian Grothoff Assigned To => Christian Grothoff
2012-10-07 14:19 Christian Grothoff Status confirmed => assigned
2012-10-12 12:38 Christian Grothoff Note Added: 0006436
2012-10-12 13:21 Christian Grothoff Note Edited: 0006436
2012-12-21 20:31 Christian Grothoff Priority low => urgent
2013-02-05 12:53 Christian Grothoff File Added: transport-ecc.diff
2013-02-05 12:53 Christian Grothoff Note Added: 0006836
2013-02-05 12:55 Christian Grothoff File Added: core_ecc.diff
2013-02-05 12:55 Christian Grothoff Note Added: 0006837
2013-02-05 14:00 Christian Grothoff File Added: combined.diff
2013-02-05 14:00 Christian Grothoff File Deleted: core_ecc.diff
2013-02-05 14:01 Christian Grothoff File Deleted: transport-ecc.diff
2013-02-05 21:49 Christian Grothoff Note Added: 0006840
2013-03-04 03:53 Christian Grothoff Note Added: 0006912
2013-03-20 13:53 Christian Grothoff Note Added: 0006995
2013-03-20 13:53 Christian Grothoff Status assigned => resolved
2013-03-20 13:53 Christian Grothoff Fixed in Version => 0.10.0
2013-03-20 13:53 Christian Grothoff Resolution open => fixed
2013-12-24 20:55 Christian Grothoff Status resolved => closed