- fix 2699
[oweals/gnunet.git] / src / util / crypto_ecc.c
index af3fe33599def8e4628c7eef6b979159fb262f67..e0764d8d54d8fbef37df8609708c1ae190c4ad90 100644 (file)
  * @file util/crypto_ecc.c
  * @brief public key cryptography (ECC) with libgcrypt
  * @author Christian Grothoff
- *
- * This is just a first, completely untested, draft hack for future ECC support.
- * TODO:
- * - declare public API in gnunet_crypto_lib (move some structs, etc.)
- * - implement gnunet-ecc binary
- * - convert existing RSA testcases
- * - adjust encoding length and other parameters
- * - actually test it!
  */
 #include "platform.h"
 #include <gcrypt.h>
 #include "gnunet_common.h"
 #include "gnunet_util_lib.h"
 
-#define EXTRA_CHECKS ALLOW_EXTRA_CHECKS
+#define EXTRA_CHECKS ALLOW_EXTRA_CHECKS || 1
 
 #define CURVE "NIST P-521"
 
@@ -67,26 +59,6 @@ struct GNUNET_CRYPTO_EccPrivateKey
 };
 
 
-/**
- * If target != size, move target bytes to the
- * end of the size-sized buffer and zero out the
- * first target-size bytes.
- *
- * @param buf original buffer
- * @param size number of bytes in the buffer
- * @param target target size of the buffer
- */
-static void
-adjust (unsigned char *buf, size_t size, size_t target)
-{
-  if (size < target)
-  {
-    memmove (&buf[target - size], buf, size);
-    memset (buf, 0, target - size);
-  }
-}
-
-
 /**
  * Free memory occupied by ECC key
  *
@@ -174,6 +146,7 @@ GNUNET_CRYPTO_ecc_key_get_public (const struct GNUNET_CRYPTO_EccPrivateKey *priv
   size_t size;
   int rc;
 
+  memset (pub, 0, sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded));
   rc = key_from_sexp (&skey, priv->sexp, "public-key", "q");
   if (rc)
     rc = key_from_sexp (&skey, priv->sexp, "private-key", "q");
@@ -186,7 +159,6 @@ GNUNET_CRYPTO_ecc_key_get_public (const struct GNUNET_CRYPTO_EccPrivateKey *priv
                  gcry_mpi_print (GCRYMPI_FMT_USG, pub->key, size, &size,
                                  skey));
   pub->len = htons (size);
-  adjust (&pub->key[0], size, GNUNET_CRYPTO_ECC_MAX_PUBLIC_KEY_LENGTH);
   gcry_mpi_release (skey);
 }
 
@@ -198,7 +170,7 @@ GNUNET_CRYPTO_ecc_key_get_public (const struct GNUNET_CRYPTO_EccPrivateKey *priv
  * @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;
@@ -248,7 +220,7 @@ GNUNET_CRYPTO_ecc_public_key_from_string (const char *enc,
                                                 sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded)))
     return GNUNET_SYSERR;
   if ( (ntohs (pub->size) != sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded)) ||
-       (ntohs (pub->len) > GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH) )
+       (ntohs (pub->len) > GNUNET_CRYPTO_ECC_SIGNATURE_DATA_ENCODING_LENGTH) )
     return GNUNET_SYSERR;
   return GNUNET_OK;
 }
@@ -270,24 +242,38 @@ decode_public_key (const struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *publicK
   size_t erroff;
   int rc;
 
-  if (ntohs (publicKey->len) > GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH) 
+  if (ntohs (publicKey->len) > GNUNET_CRYPTO_ECC_SIGNATURE_DATA_ENCODING_LENGTH) 
   {
     GNUNET_break (0);
     return NULL;
   }
-  size = ntohs (publicKey->size);
+  size = ntohs (publicKey->len);
   if (0 != (rc = gcry_mpi_scan (&q, GCRYMPI_FMT_USG, publicKey->key, size, &size)))
   {
     LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
     return NULL;
   }
-  rc = gcry_sexp_build (&result, &erroff, "(public-key(ecc((curve \"" CURVE "\")(q %m)))", q);
+
+  rc = gcry_sexp_build (&result, &erroff, 
+                       "(public-key(ecdsa(curve \"" CURVE "\")(q %m)))",
+                       q);
   gcry_mpi_release (q);
   if (0 != rc)
   {
     LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc);  /* erroff gives more info */
     return NULL;
   }
+  // FIXME: is this key expected to pass pk_testkey?
+#if 0
+#if EXTRA_CHECKS 
+  if (0 != (rc = gcry_pk_testkey (result)))
+  {
+    LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_testkey", rc);
+    gcry_sexp_release (result);
+    return NULL;
+  }
+#endif
+#endif
   return result;
 }
 
@@ -309,7 +295,7 @@ GNUNET_CRYPTO_ecc_encode_key (const struct GNUNET_CRYPTO_EccPrivateKey *key)
   size_t size;
 
 #if EXTRA_CHECKS
-  if (gcry_pk_testkey (hostkey->sexp))
+  if (0 != gcry_pk_testkey (key->sexp))
   {
     GNUNET_break (0);
     return NULL;
@@ -324,7 +310,7 @@ GNUNET_CRYPTO_ecc_encode_key (const struct GNUNET_CRYPTO_EccPrivateKey *key)
     return NULL;
   }
   GNUNET_assert (size < 65536 - sizeof (uint16_t));
-  be = htons ((uint16_t) size);
+  be = htons ((uint16_t) size + (sizeof (be)));
   memcpy (buf, &be, sizeof (be));
   size += sizeof (be);
   retval = GNUNET_malloc (size);
@@ -354,8 +340,9 @@ GNUNET_CRYPTO_ecc_decode_key (const char *buf,
   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],
@@ -380,20 +367,34 @@ GNUNET_CRYPTO_ecc_decode_key (const char *buf,
  *
  * @return fresh private key
  */
-static struct GNUNET_CRYPTO_EccPrivateKey *
-ecc_key_create ()
+struct GNUNET_CRYPTO_EccPrivateKey *
+GNUNET_CRYPTO_ecc_key_create ()
 {
   struct GNUNET_CRYPTO_EccPrivateKey *ret;
   gcry_sexp_t s_key;
   gcry_sexp_t s_keyparam;
+  int rc;
 
-  GNUNET_assert (0 ==
-                 gcry_sexp_build (&s_keyparam, NULL,
-                                  "(genkey(ecc(curve \"" CURVE "\")))"));
-  GNUNET_assert (0 == gcry_pk_genkey (&s_key, s_keyparam));
+  if (0 != (rc = gcry_sexp_build (&s_keyparam, NULL,
+                                  "(genkey(ecdsa(curve \"" CURVE "\")))")))
+  {
+    LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc);
+    return NULL;
+  }
+  if (0 != (rc = gcry_pk_genkey (&s_key, s_keyparam)))
+  {
+    LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_genkey", rc);
+    gcry_sexp_release (s_keyparam);
+    return NULL;
+  }
   gcry_sexp_release (s_keyparam);
 #if EXTRA_CHECKS
-  GNUNET_assert (0 == gcry_pk_testkey (s_key));
+  if (0 != (rc = gcry_pk_testkey (s_key)))
+  {
+    LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_testkey", rc);
+    gcry_sexp_release (s_key);
+    return NULL;
+  }
 #endif
   ret = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EccPrivateKey));
   ret->sexp = s_key;
@@ -417,7 +418,7 @@ try_read_key (const char *filename)
   if (GNUNET_YES != GNUNET_DISK_file_test (filename))
     return NULL;
 
-  /* hostkey file exists already, read it! */
+  /* key file exists already, read it! */
   if (NULL == (fd = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ,
                                           GNUNET_DISK_PERM_NONE)))
   {
@@ -555,7 +556,7 @@ GNUNET_CRYPTO_ecc_key_create_from_file (const char *filename)
     }
     LOG (GNUNET_ERROR_TYPE_INFO,
          _("Creating a new private key.  This may take a while.\n"));
-    ret = ecc_key_create ();
+    ret = GNUNET_CRYPTO_ecc_key_create ();
     GNUNET_assert (ret != NULL);
     enc = GNUNET_CRYPTO_ecc_encode_key (ret);
     GNUNET_assert (enc != NULL);
@@ -571,12 +572,9 @@ GNUNET_CRYPTO_ecc_key_create_from_file (const char *filename)
     GNUNET_assert (GNUNET_YES == GNUNET_DISK_file_close (fd));
     GNUNET_CRYPTO_ecc_key_get_public (ret, &pub);
     GNUNET_CRYPTO_hash (&pub, sizeof (pub), &pid.hashPubKey);
-    LOG (GNUNET_ERROR_TYPE_INFO,
-         _("I am host `%s'.  Stored new private key in `%s'.\n"),
-         GNUNET_i2s (&pid), filename);
     return ret;
   }
-  /* hostkey file exists already, read it! */
+  /* key file exists already, read it! */
   fd = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ,
                               GNUNET_DISK_PERM_NONE);
   if (NULL == fd)
@@ -600,7 +598,7 @@ GNUNET_CRYPTO_ecc_key_create_from_file (const char *filename)
              STRERROR (ec));
         LOG (GNUNET_ERROR_TYPE_ERROR,
              _
-             ("This may be ok if someone is currently generating a hostkey.\n"));
+             ("This may be ok if someone is currently generating a private key.\n"));
       }
       short_wait ();
       continue;
@@ -621,7 +619,7 @@ GNUNET_CRYPTO_ecc_key_create_from_file (const char *filename)
       fs = 0;
     if (fs < sizeof (struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded))
     {
-      /* maybe we got the read lock before the hostkey generating
+      /* maybe we got the read lock before the key generating
        * process had a chance to get the write lock; give it up! */
       if (GNUNET_YES !=
           GNUNET_DISK_file_unlock (fd, 0,
@@ -631,12 +629,12 @@ GNUNET_CRYPTO_ecc_key_create_from_file (const char *filename)
       {
         LOG (GNUNET_ERROR_TYPE_ERROR,
              _
-             ("When trying to read hostkey file `%s' I found %u bytes but I need at least %u.\n"),
+             ("When trying to read key file `%s' I found %u bytes but I need at least %u.\n"),
              filename, (unsigned int) fs,
              (unsigned int) sizeof (struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded));
         LOG (GNUNET_ERROR_TYPE_ERROR,
              _
-             ("This may be ok if someone is currently generating a hostkey.\n"));
+             ("This may be ok if someone is currently generating a key.\n"));
       }
       short_wait ();                /* wait a bit longer! */
       continue;
@@ -647,7 +645,7 @@ GNUNET_CRYPTO_ecc_key_create_from_file (const char *filename)
   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,
@@ -668,9 +666,6 @@ GNUNET_CRYPTO_ecc_key_create_from_file (const char *filename)
   {
     GNUNET_CRYPTO_ecc_key_get_public (ret, &pub);
     GNUNET_CRYPTO_hash (&pub, sizeof (pub), &pid.hashPubKey);
-    LOG (GNUNET_ERROR_TYPE_INFO,
-         _("I am host `%s'.  Read private key from `%s'.\n"), GNUNET_i2s (&pid),
-         filename);
   }
   return ret;
 }
@@ -836,7 +831,6 @@ GNUNET_CRYPTO_ecc_key_create_start (const char *filename,
 {
   struct GNUNET_CRYPTO_EccKeyGenerationContext *gc;
   struct GNUNET_CRYPTO_EccPrivateKey *pk;
-  const char *weak_random;
 
   if (NULL != (pk = try_read_key (filename)))
   {
@@ -864,10 +858,6 @@ GNUNET_CRYPTO_ecc_key_create_start (const char *filename,
     GNUNET_free (gc);
     return NULL;
   }
-  weak_random = NULL;
-  if (GNUNET_YES ==
-      GNUNET_CRYPTO_random_is_weak ())
-    weak_random = "-w";
   gc->gnunet_ecc = GNUNET_OS_start_process (GNUNET_NO,
                                            GNUNET_OS_INHERIT_STD_ERR,
                                            NULL, 
@@ -875,7 +865,6 @@ GNUNET_CRYPTO_ecc_key_create_start (const char *filename,
                                            "gnunet-ecc",
                                            "gnunet-ecc",                                           
                                            gc->filename,
-                                           weak_random,
                                            NULL);
   if (NULL == gc->gnunet_ecc)
   {
@@ -898,15 +887,15 @@ GNUNET_CRYPTO_ecc_key_create_start (const char *filename,
 
 
 /**
- * Setup a hostkey file for a peer given the name of the
+ * Setup a key file for a peer given the name of the
  * configuration file (!).  This function is used so that
  * at a later point code can be certain that reading a
- * hostkey is fast (for example in time-dependent testcases).
+ * key is fast (for example in time-dependent testcases).
  *
  * @param cfg_name name of the configuration file to use
  */
 void
-GNUNET_CRYPTO_ecc_setup_hostkey (const char *cfg_name)
+GNUNET_CRYPTO_ecc_setup_key (const char *cfg_name)
 {
   struct GNUNET_CONFIGURATION_Handle *cfg;
   struct GNUNET_CRYPTO_EccPrivateKey *pk;
@@ -926,109 +915,6 @@ GNUNET_CRYPTO_ecc_setup_hostkey (const char *cfg_name)
 }
 
 
-/**
- * Encrypt a block with the public key of another host that uses the
- * same cipher.
- *
- * @param block the block to encrypt
- * @param size the size of block
- * @param publicKey the encoded public key used to encrypt
- * @param target where to store the encrypted block
- * @returns GNUNET_SYSERR on error, GNUNET_OK if ok
- */
-int
-GNUNET_CRYPTO_ecc_encrypt (const void *block, size_t size,
-                           const struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded
-                           *publicKey,
-                           struct GNUNET_CRYPTO_EccEncryptedData *target)
-{
-  gcry_sexp_t result;
-  gcry_sexp_t data;
-  gcry_sexp_t psexp;
-  gcry_mpi_t val;
-  size_t isize;
-  size_t erroff;
-
-  GNUNET_assert (size <= sizeof (struct GNUNET_HashCode));
-  if (! (psexp = decode_public_key (publicKey)))
-    return GNUNET_SYSERR;
-  isize = size;
-  GNUNET_assert (0 ==
-                 gcry_mpi_scan (&val, GCRYMPI_FMT_USG, block, isize, &isize));
-  GNUNET_assert (0 ==
-                 gcry_sexp_build (&data, &erroff,
-                                  "(data (flags pkcs1)(value %m))", val));
-  gcry_mpi_release (val);
-  GNUNET_assert (0 == gcry_pk_encrypt (&result, data, psexp));
-  gcry_sexp_release (data);
-  gcry_sexp_release (psexp);
-  isize = gcry_sexp_sprint (result, 
-                           GCRYSEXP_FMT_DEFAULT,
-                           target->encoding,
-                           GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH);
-  if (0 == isize)
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  target->size = htons ((uint16_t) isize);
-  /* padd with zeros */
-  memset (&target->encoding[isize], 0, GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH - isize);
-  return GNUNET_OK;
-}
-
-
-/**
- * Decrypt a given block with the hostkey.
- *
- * @param key the key with which to decrypt this block
- * @param block the data to decrypt, encoded as returned by encrypt
- * @param result pointer to a location where the result can be stored
- * @param max the maximum number of bits to store for the result, if
- *        the decrypted block is bigger, an error is returned
- * @return the size of the decrypted block, -1 on error
- */
-ssize_t
-GNUNET_CRYPTO_ecc_decrypt (const struct GNUNET_CRYPTO_EccPrivateKey *key,
-                           const struct GNUNET_CRYPTO_EccEncryptedData *block,
-                           void *result, size_t max)
-{
-  gcry_sexp_t resultsexp;
-  gcry_sexp_t data;
-  size_t erroff;
-  size_t size;
-  gcry_mpi_t val;
-  unsigned char *endp;
-
-#if EXTRA_CHECKS
-  GNUNET_assert (0 == gcry_pk_testkey (key->sexp));
-#endif
-  size = ntohs (block->size);
-  GNUNET_assert (0 ==
-                 gcry_sexp_sscan (&data,
-                                 &erroff,
-                                 block->encoding, size));
-  GNUNET_assert (0 == gcry_pk_decrypt (&resultsexp, data, key->sexp));
-  gcry_sexp_release (data);
-  /* resultsexp has format "(value %m)" */
-  GNUNET_assert (NULL !=
-                 (val = gcry_sexp_nth_mpi (resultsexp, 1, GCRYMPI_FMT_USG)));
-  gcry_sexp_release (resultsexp);
-  size = max + GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH * 2;
-  {
-    unsigned char tmp[size];
-
-    GNUNET_assert (0 == gcry_mpi_print (GCRYMPI_FMT_USG, tmp, size, &size, val));
-    gcry_mpi_release (val);
-    endp = tmp;
-    endp += (size - max);
-    size = max;
-    memcpy (result, endp, size);
-  }
-  return size;
-}
-
-
 /**
  * Convert the data specified in the given purpose argument to an
  * S-expression suitable for signature operations.
@@ -1039,12 +925,12 @@ GNUNET_CRYPTO_ecc_decrypt (const struct GNUNET_CRYPTO_EccPrivateKey *key,
 static gcry_sexp_t
 data_to_pkcs1 (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose)
 {
-  struct GNUNET_HashCode hc;
+  struct GNUNET_CRYPTO_ShortHashCode hc;
   size_t bufSize;
   gcry_sexp_t data;
 
-  GNUNET_CRYPTO_hash (purpose, ntohl (purpose->size), &hc);
-#define FORMATSTRING "(4:data(5:flags5:pkcs1)(4:hash6:sha51264:0123456789012345678901234567890123456789012345678901234567890123))"
+  GNUNET_CRYPTO_short_hash (purpose, ntohl (purpose->size), &hc);
+#define FORMATSTRING "(4:data(5:flags3:raw)(5:value32:01234567890123456789012345678901))"
   bufSize = strlen (FORMATSTRING) + 1;
   {
     char buff[bufSize];
@@ -1053,8 +939,8 @@ data_to_pkcs1 (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose)
     memcpy (&buff
            [bufSize -
             strlen
-            ("0123456789012345678901234567890123456789012345678901234567890123))")
-            - 1], &hc, sizeof (struct GNUNET_HashCode));
+            ("01234567890123456789012345678901))")
+            - 1], &hc, sizeof (struct GNUNET_CRYPTO_ShortHashCode));
     GNUNET_assert (0 == gcry_sexp_new (&data, buff, bufSize, 0));
   }
 #undef FORMATSTRING
@@ -1078,22 +964,28 @@ GNUNET_CRYPTO_ecc_sign (const struct GNUNET_CRYPTO_EccPrivateKey *key,
   gcry_sexp_t result;
   gcry_sexp_t data;
   size_t ssize;
+  int rc;
 
   data = data_to_pkcs1 (purpose);
-  GNUNET_assert (0 == gcry_pk_sign (&result, data, key->sexp));
+  if (0 != (rc = gcry_pk_sign (&result, data, key->sexp)))
+  {
+    LOG (GNUNET_ERROR_TYPE_WARNING,
+         _("ECC signing failed at %s:%d: %s\n"), __FILE__,
+         __LINE__, gcry_strerror (rc));
+  }
   gcry_sexp_release (data);
   ssize = gcry_sexp_sprint (result, 
                            GCRYSEXP_FMT_DEFAULT,
                            sig->sexpr,
-                           GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH);
+                           GNUNET_CRYPTO_ECC_SIGNATURE_DATA_ENCODING_LENGTH);
   if (0 == ssize)
   {
     GNUNET_break (0);
     return GNUNET_SYSERR;
   }
-  sig->size = htons ((uint16_t) ssize);
+  sig->size = htons ((uint16_t) (ssize + sizeof (uint16_t)));
   /* padd with zeros */
-  memset (&sig->sexpr[ssize], 0, GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH - ssize);
+  memset (&sig->sexpr[ssize], 0, GNUNET_CRYPTO_ECC_SIGNATURE_DATA_ENCODING_LENGTH - ssize);
   gcry_sexp_release (result);
   return GNUNET_OK;
 }
@@ -1126,11 +1018,13 @@ GNUNET_CRYPTO_ecc_verify (uint32_t purpose,
   if (purpose != ntohl (validate->purpose))
     return GNUNET_SYSERR;       /* purpose mismatch */
   size = ntohs (sig->size);
-  if (size > GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH - sizeof (uint16_t))
+  if ( (size < sizeof (uint16_t)) ||
+       (size > GNUNET_CRYPTO_ECC_SIGNATURE_DATA_ENCODING_LENGTH - sizeof (uint16_t)) )
     return GNUNET_SYSERR; /* size out of range */
   data = data_to_pkcs1 (validate);
   GNUNET_assert (0 ==
-                 gcry_sexp_sscan (&sigdata, &erroff, sig->sexpr, size));
+                 gcry_sexp_sscan (&sigdata, &erroff, 
+                                 sig->sexpr, size - sizeof (uint16_t)));
   if (! (psexp = decode_public_key (publicKey)))
   {
     gcry_sexp_release (data);
@@ -1152,4 +1046,91 @@ GNUNET_CRYPTO_ecc_verify (uint32_t purpose,
 }
 
 
+/**
+ * Derive key material from a public and a private ECC key.
+ *
+ * @param key private key to use for the ECDH (x)
+ * @param pub public key to use for the ECDY (yG)
+ * @param key_material where to write the key material (xyG)
+ * @return GNUNET_SYSERR on error, GNUNET_OK on success
+ */
+int
+GNUNET_CRYPTO_ecc_ecdh (const struct GNUNET_CRYPTO_EccPrivateKey *key,
+                        const struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *pub,
+                        struct GNUNET_HashCode *key_material)
+{ 
+  size_t size;
+  size_t slen;
+  int rc;
+  gcry_sexp_t data;  
+  unsigned char sdata_buf[2048]; /* big enough to print 'sdata' and 'r_sig' */
+
+  /* first, extract the q value from the public key */
+  {
+    gcry_sexp_t psexp;
+    gcry_mpi_t sdata;
+    
+    if (! (psexp = decode_public_key (pub)))
+      return GNUNET_SYSERR;
+    rc = key_from_sexp (&sdata, psexp, "public-key", "q");
+    if (rc)
+      rc = key_from_sexp (&sdata, psexp, "ecc", "q");
+    GNUNET_assert (0 == rc);  
+    gcry_sexp_release (psexp);
+    size = sizeof (sdata_buf);
+    GNUNET_assert (0 ==
+                  gcry_mpi_print (GCRYMPI_FMT_USG, sdata_buf, size, &size,
+                                  sdata));
+    gcry_mpi_release (sdata);
+  }
+  /* convert q value into an S-expression -- whatever format libgcrypt wants,
+     re-using format from sign operation for now... */
+  {
+    char *sexp_string;
+
+#define FORMATPREFIX "(4:data(5:flags3:raw)(5:value%u:"
+#define FORMATPOSTFIX "))"
+    sexp_string = GNUNET_malloc (strlen (FORMATPREFIX) + size + 12 +
+                                 strlen (FORMATPOSTFIX) + 1);
+    GNUNET_snprintf (sexp_string,
+                    strlen (FORMATPREFIX) + 12,
+                    FORMATPREFIX,
+                    size);
+    slen = strlen (sexp_string);
+    memcpy (&sexp_string[slen],
+           sdata_buf, 
+           size);
+    memcpy (&sexp_string[slen + size],
+           FORMATPOSTFIX,
+           strlen (FORMATPOSTFIX) + 1);  
+    GNUNET_assert (0 == gcry_sexp_new (&data, 
+                                      sexp_string, 
+                                      slen + size + strlen (FORMATPOSTFIX), 
+                                      0));
+    GNUNET_free (sexp_string);
+  }
+  /* then call the 'multiply' function, hoping it simply multiplies the points;
+     here we need essentially a WRAPPER around _gcry_mpi_ex_mul_point! - FIXME-WK!*/
+#if WK
+  {
+    gcry_sexp_t result;
+    
+    rc = gcry_ecc_mul_point (&result, data /* scalar */, key->sexp /* point and ctx */);
+    GNUNET_assert (0 == rc);
+    slen = gcry_sexp_sprint (result, GCRYSEXP_FMT_DEFAULT, sdata_buf, sizeof (sdata_buf));
+    GNUNET_assert (0 != slen);
+  }
+#else
+  /* use broken version, insecure! */
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("To be implemented: not secure at the moment, please read README"));
+  slen = sprintf ((char*) sdata_buf, "FIXME-this is not key material");
+#endif
+  gcry_sexp_release (data);
+
+  /* finally, get a string of the resulting S-expression and hash it to generate the key material */
+  GNUNET_CRYPTO_hash (sdata_buf, slen, key_material);
+  return GNUNET_OK;
+}
+
+
 /* end of crypto_ecc.c */